Compare commits

...

1 Commits

Author SHA1 Message Date
Petru Conduraru c424d1b480 ifcgeom: resolve default materials through the schema supertype chain (#473)
get_default_style did an exact-name map lookup, so subtypes like
IfcWallStandardCase or IfcSlabStandardCase never inherited the default
material of their parent type, falling through to the generic grey
DefaultMaterial instead. This was reported in 2018 (#473) and the fix
direction (walk up the supertype chain once the schema runtime made
supertypes available) was agreed there, but never implemented.

On an exact-match miss the lookup now walks decl->supertype() until a
mapped ancestor is found, caching the result under the leaf name for
O(1) subsequent lookups. The product's user-facing type string is
deliberately untouched; only the style lookup gained inheritance.
Applies equally to the built-in defaults and --default-material-file.

This change was written with AI assistance.
2026-07-21 17:43:06 +03:00
5 changed files with 52 additions and 7 deletions
+1 -1
View File
@@ -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 = "";
+11 -1
View File
@@ -30,8 +30,18 @@
#include <sstream>
#include <memory>
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);
+1 -1
View File
@@ -137,7 +137,7 @@ IfcGeom::Representation::Triangulation::Triangulation(const BRep& shape_model)
}
if (settings().get<ifcopenshell::geometry::settings::ApplyDefaultMaterials>().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();
+17 -3
View File
@@ -25,6 +25,10 @@
#include <map>
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() {}
+22 -1
View File
@@ -1,4 +1,5 @@
#include "../ifcgeom/IfcGeomRenderStyles.h"
#include "../ifcparse/IfcSchema.h"
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
@@ -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<std::mutex> 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;
}