helpers: port util.element.get_predefined_type; show it in properties

Add src/helpers/element.{h,cpp}, a schema-dispatched C++ port of
ifcopenshell.util.element.get_predefined_type: prefers the associated
type element's predefined type (IsTypedBy / IsDefinedBy), falls back to
ElementType / ProcessType when USERDEFINED, then the occurrence's own
PredefinedType / ObjectType. Attribute reads are by-name so they work
across the IfcElement / IfcType* subtypes that carry these attributes.

Wire it into the properties panel entity summary: live IFC entities show
their real predefined type; geometry-only elements (a .ifcview loaded
without its .ifc/.rdb) show "N/A". Clears the placeholder so a stale
predefined type no longer leaks once a project is loaded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-07-08 14:48:09 +10:00
parent 75c9da5098
commit c5ea612110
4 changed files with 197 additions and 0 deletions
@@ -25,6 +25,8 @@
#include "../../ElementRegistry.h"
#include "../../SessionState.h"
#include "element.h" // helpers: get_predefined_type
namespace bonsaiviewer::modules::properties {
PropertiesPanelView::PropertiesPanelView(PropertiesPanel* widget,
@@ -90,9 +92,17 @@ void PropertiesPanelView::refresh(uint32_t object_id) {
return;
}
// A real project is loaded — don't leak the placeholder predefined type.
// It's populated below from live IFC data, or set to "N/A" for
// geometry-only elements that have no data to read it from.
state.entity.predefined_type.clear();
auto entity = registry->findEntity(object_id);
if (entity) {
state.entity.entity_class = QString::fromStdString(entity->declaration().name());
if (auto predefined_type = get_predefined_type(*entity)) {
state.entity.predefined_type = QString::fromStdString(*predefined_type);
}
if (!state.property_sets.isEmpty() && !state.property_sets[1].rows.isEmpty()) {
state.property_sets[1].rows[0].value = state.entity.entity_class;
}
@@ -104,6 +114,8 @@ void PropertiesPanelView::refresh(uint32_t object_id) {
auto info = registry->findBasicElementInfo(object_id);
if (info && !info->type.isEmpty()) {
state.entity.entity_class = info->type;
// Geometry only — no live IFC entity to read a predefined type from.
state.entity.predefined_type = "N/A";
if (!state.property_sets.isEmpty() && !state.property_sets[1].rows.isEmpty()) {
state.property_sets[1].rows[0].value = info->type;
}