/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "IfcWhereRulesValidationHealer.h" #include "IfcCurve.h" #include "IfcProfileDef.h" #include "IfcEntity.h" using namespace OdIfc; using namespace OdDAI; ODRX_CONS_DEFINE_MEMBERS(WhereRulesHealerEntityValidation, OdDAI::ValidationHealer, RXIMPL_CONSTR); void WhereRulesHealerEntityValidation::initMap(OdDAI::SchemaPtr pSchema) { const auto& mapping = s_rulesMap.find(pSchema->name()); if (mapping != s_rulesMap.end()) return; std::map healersMap; { OdDAI::EntityPtr pEnt = pSchema->find("ifcbuildingelementproxy"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("hasobjectname"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("wr1"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcconditioncriterion"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr1"); if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcmove"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr3"); if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcprocess"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr3"); if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcpropertyset"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr31"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("existsname"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcproject"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr31"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("hasname"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcproxy"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr1"); if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifctask"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr3"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("hasname"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifctypeobject"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr1"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("namerequired"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcsweptareasolid"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr22"); if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerProfileType(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcexternalreference"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr1"); if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcdocumentreference"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr1"); if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerName(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcpropertyset"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr32"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("uniquepropertynames"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerPropertySet(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcobject"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("wr1"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("uniquepropertysetnames"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerPropertySet(); } } } { OdDAI::EntityPtr pEnt = pSchema->find("ifcshaperepresentation"); if (!pEnt.isNull()) { OdDAI::WhereRulePtr whereRule = pEnt->findWhereRule("correctcontext"); if (whereRule.isNull()) { whereRule = pEnt->findWhereRule("wr21"); } if (!whereRule.isNull()) { healersMap[whereRule] = new WhereRuleHealerCorrectContext(); } } } { OdDAI::EntityPtr pOpenProfileEnt = pSchema->find("ifcarbitraryopenprofiledef"); OdDAI::EntityPtr pClosedProfileEnt = pSchema->find("ifcarbitraryclosedprofiledef"); WhereRuleHealerPtr pProfileHealer = new ArbitraryProfileCurve2dValidationHealer(); if (pOpenProfileEnt) { OdDAI::WhereRulePtr whereRule = pOpenProfileEnt->findWhereRule("wr1"); if (!whereRule.isNull()) { healersMap[whereRule] = pProfileHealer; } } if (pClosedProfileEnt) { OdDAI::WhereRulePtr whereRule = pClosedProfileEnt->findWhereRule("wr1"); if (!whereRule.isNull()) { healersMap[whereRule] = pProfileHealer; } } } s_rulesMap[pSchema->name()] = healersMap; } OdDAI::Logical WhereRulesHealerEntityValidation::heal(OdDAI::Model* model, OdDAI::ValidationTask::InvalidValidationParamsBase* invalidParams) { OdDAI::ValidationTask::InvalidRulesValidationParams* invalidInstances = dynamic_cast(invalidParams); OdArray& whereRules = invalidInstances->invalidWhereRules; OdDAI::SchemaPtr pSchema = model->underlyingSchema(); initMap(pSchema); OdDAI::Logical res = OdDAI::Logical::False; for (auto& schemaMap : s_rulesMap) { if (pSchema->name().compare(schemaMap.first) == 0) { std::map& healersMap = schemaMap.second; for (auto& whereRule : whereRules) { const auto& pHealer = healersMap.find(whereRule); if (pHealer != healersMap.cend()) { OdDAI::WhereRulePtr healRule = pHealer->first; res = pHealer->second->heal(invalidInstances->invalidInst, healRule); } } } } return res; } OdDAI::Logical WhereRuleHealerName::heal(OdDAI::ApplicationInstancePtr inst, OdDAI::WhereRulePtr rule) { if (inst->testAttr("name") == true) return OdDAI::Logical::True; OdAnsiString name; inst->getAttr("name") >> name; if (name.isEmpty()) { inst->putAttr("name", OdString("default_name")); return OdDAI::Logical::True; } return OdDAI::Logical::False; } OdDAI::Logical WhereRuleHealerProfileType::heal(OdDAI::ApplicationInstancePtr inst, OdDAI::WhereRulePtr rule) { OdDAIObjectId sweptArea; if (inst->getAttr("sweptarea") >> sweptArea) { if (sweptArea.isValid()) { OdDAI::ApplicationInstancePtr pArea = sweptArea.openObject(); pArea->putAttr("profiletype", static_cast("AREA")); return OdDAI::Logical::True; } } return OdDAI::Logical::False; } OdDAI::Logical WhereRuleHealerPropertySet::heal(OdDAI::ApplicationInstancePtr inst, OdDAI::WhereRulePtr rule) { OdDAIObjectIds relDefindeProperties; inst->getAttr("isdefinedby") >> relDefindeProperties; #ifdef _HEAL_BY_RENAME unsigned int counter = 1; #endif // _HEAL_BY_RENAME OdDAI::ModelPtr pModel = inst->owningModel(); pModel->promoteModelToReadWrite(); OdDAIObjectIds forRemooval; for (unsigned int i = 0; i < relDefindeProperties.size() - 1; ++i) { OdDAI::ApplicationInstancePtr relDefineProperty1 = relDefindeProperties.at(i).openObject(); OdDAIObjectId propertySet1; relDefineProperty1->getAttr("relatingpropertydefinition") >> propertySet1; OdDAI::ApplicationInstancePtr propertySetInst1 = propertySet1.openObject(); OdAnsiString name1; propertySetInst1->getAttr("name") >> name1; OdDAI::SetOfOdDAIObjectId* propertyDefinitions1; propertySetInst1->getAttr("hasproperties") >> propertyDefinitions1; for (unsigned int j = i + 1; j < relDefindeProperties.size(); ++j) { OdDAIObjectId idRealDefinedByPropertiesCopyFrom = relDefindeProperties.at(j); OdDAI::ApplicationInstancePtr relDefinePropertyCopyFrom = idRealDefinedByPropertiesCopyFrom.openObject(); OdDAIObjectId propertySetCopyFrom; relDefinePropertyCopyFrom->getAttr("relatingpropertydefinition") >> propertySetCopyFrom; OdDAI::ApplicationInstancePtr propertySetInst2 = propertySetCopyFrom.openObject(); OdAnsiString name2; propertySetInst2->getAttr("name") >> name2; if (name1.iCompare(name2) == 0) { #ifdef _HEAL_BY_RENAME name2 += OdAnsiString("_healedName_"); name2 += std::to_string(counter).c_str(); propertySetInst2->putAttr("name", name2); ++counter; #else OdDAI::SetOfOdDAIObjectId* propertyDefinitionsCopyFrom; propertySetInst2->getAttr("hasproperties") >> propertyDefinitionsCopyFrom; if (!propertyDefinitionsCopyFrom->empty()) { for (OdDAIObjectId definitionId : propertyDefinitionsCopyFrom->getArray()) propertyDefinitions1->Add(definitionId); if(!forRemooval.contains(idRealDefinedByPropertiesCopyFrom)) forRemooval.append(idRealDefinedByPropertiesCopyFrom); } #endif // _HEAL_BY_RENAME } } } for (OdDAIObjectId duplicate : forRemooval) { OdDAI::ApplicationInstancePtr relDefinePropertyDuplicate = duplicate.openObject(); OdDAI::SetOfOdDAIObjectId* relatedObjectsDuplicate; relDefinePropertyDuplicate->getAttr("relatedobjects") >> relatedObjectsDuplicate; relatedObjectsDuplicate->Remove(OdDAIObjectId(inst->id())); if (relatedObjectsDuplicate->empty()) { pModel->deleteInstance(relDefinePropertyDuplicate); } } return OdDAI::Logical::True; } OdDAI::Logical WhereRuleHealerCorrectContext::heal(OdDAI::ApplicationInstancePtr inst, OdDAI::WhereRulePtr rule) { OdDAI::ModelPtr pModel = inst->owningModel(); OdAnsiString representationItentifier; inst->getAttr("representationidentifier") >> representationItentifier; OdAnsiString representationType; inst->getAttr("representationtype") >> representationType; const OdDAI::SetOfOdDAIObjectId* ext = pModel->getEntityExtent("ifcgeometricrepresentationcontext"); const OdDAIObjectIds& contexts = ext->getArray(); for (const OdDAIObjectId& context : contexts) { OdDAI::ApplicationInstancePtr pContext = context.openObject(); OdAnsiString contextIdentifier; pContext->getAttr("contextidentifier") >> contextIdentifier; if (contextIdentifier.iCompare(representationItentifier) == 0) { inst->putAttr("contextofitems", context); return OdDAI::Logical::True; } } for (const OdDAIObjectId& context : contexts) { OdDAI::ApplicationInstancePtr pContext = context.openObject(); OdAnsiString contextName = pContext->getInstanceType()->name(); if (pContext->isInstanceOf("ifcgeometricrepresentationcontext")) { OdAnsiString contextType; pContext->getAttr("contexttype") >> contextType; if (contextType.iCompare("Model") == 0) { inst->putAttr("contextofitems", context); return OdDAI::Logical::True; } } } OdDAI::ApplicationInstancePtr pPoint = pModel->createEntityInstance("ifccartesianpoint"); OdArray coordinates; coordinates.append(0.); coordinates.append(0.); coordinates.append(0.); pPoint->putAttr("coordinates", coordinates); OdDAIObjectId pointId = pModel->appendEntityInstance(pPoint); OdDAI::ApplicationInstancePtr pPlacement = pModel->createEntityInstance("ifcaxis2placement3d"); pPlacement->putAttr("location", pointId); OdDAIObjectId placementId = pModel->appendEntityInstance(pPlacement); OdDAI::ApplicationInstancePtr pParentContext = pModel->createEntityInstance("ifcgeometricrepresentationcontext"); pParentContext->putAttr("contexttype", static_cast("Model")); pParentContext->putAttr("worldcoordinatesystem", placementId); pParentContext->putAttr("coordinatespacedimension", 3); OdDAIObjectId parentContextId = pModel->appendEntityInstance(pParentContext); inst->putAttr("contextofitems", parentContextId); return OdDAI::Logical::True; } namespace { OdDAIObjectId heal3dTo2d(OdIfcInstance* pInst); void checkExplicitAttributes(OdIfcInstance* pInst, Entity* pEntityType) { OdDAIObjectId id; const OdArray& explicitAttributes = pEntityType->explicitAttributes(); for (OdDAI::ExplicitAttribute* pAttr : explicitAttributes) { OdDAI::BaseTypePtr domain = pAttr->domain(); OdTCKind kind = domain->type()->kind(); if (kind == tkSequence) { if (const OdDAI::AggregationType* at = domain->aggregationType()) { const OdTCKind elementKind = at->elementType()->type()->kind(); if (elementKind == tkObjectId || elementKind == tkSelect) { OdDAI::Aggr* pAggr{ nullptr }; pInst->getAttr(pAttr) >> pAggr; if (pAggr && pAggr->getMemberCount()) { for (auto it = pAggr->createIterator(); it->next();) { it->getCurrentMember() >> id; if (id.isValid()) { if (OdDAIObjectId newId = heal3dTo2d(OdIfcInstance::cast(id.openObject()))) { it->putCurrentMember(newId); } } } } } } } else if (kind == tkObjectId || kind == tkSelect) { if (pInst->getAttr(pAttr) >> id && id.isValid()) { if (OdDAIObjectId newId = heal3dTo2d(OdIfcInstance::cast(id.openObject()))) { pInst->putAttr(pAttr, newId); } } } } } OdDAIObjectId heal3dTo2d(OdIfcInstance* pInst) { if (pInst->isKindOf(kIfcPoint)) { OdDAI::List* pCoords{ nullptr }; pInst->getAttr(kCoordinates) >> pCoords; if (pCoords && pCoords->getMemberCount() == 3) { pCoords->removeByIndex(2); } } else if (pInst->isKindOf(kIfcDirection)) { OdDAI::List* pDirectionRatios{ nullptr }; pInst->getAttr(kDirectionRatios) >> pDirectionRatios; if (pDirectionRatios && pDirectionRatios->getMemberCount() == 3) { pDirectionRatios->removeByIndex(2); } } else if (pInst->isKindOf(kIfcAxis2Placement3D)) { OdDAIObjectId locationId; if (pInst->getAttr(kLocation) >> locationId && locationId.isValid()) heal3dTo2d(OdIfcInstance::cast(locationId.openObject())); OdDAIObjectId refDirectionId; if (pInst->getAttr(kRefDirection) >> refDirectionId && refDirectionId.isValid()) heal3dTo2d(OdIfcInstance::cast(refDirectionId.openObject())); OdDAI::ModelPtr pModel = pInst->owningModel(); OdIfcInstancePtr pNewInst = pModel->createEntityInstance("ifcaxis2placement2d"); pNewInst->putAttr(kLocation, locationId); pNewInst->putAttr(kRefDirection, refDirectionId); return pModel->insertEntityInstance(pNewInst, OdDAIObjectId(pInst->id()).getHandle()); } else { auto pEntityType = pInst->getInstanceType(); for (const auto& supertype : pEntityType->supertypes().getArray()) { checkExplicitAttributes(pInst, supertype); } checkExplicitAttributes(pInst, pEntityType); } return {}; } } OdDAI::Logical ArbitraryProfileCurve2dValidationHealer::heal(OdDAI::ApplicationInstancePtr inst, OdDAI::WhereRulePtr rule) { OdDAI::Logical result = Logical::True; OdDAIObjectId id; OdIfcInstancePtr pInvalidInst = OdIfcInstance::cast(inst); pInvalidInst->getAttr(kOuterCurve) >> id; OdIfcInstancePtr pCurveInst = id.openObject(); if (pCurveInst->isKindOf(kIfcCurve)) { OdIfcCurvePtr pCurve = OdIfcCurve::cast(pCurveInst->owningStepFile()->getCompound(id)); if (pCurve.isNull()) { ODA_FAIL_M("OdIfcCurvePtr is NULL."); return Logical::False; } OdGePoint3d testPoint; pCurve->getGeCurve()->hasStartPoint(testPoint); if (OdNonZero(testPoint.z)) { //Change position, if Z coord is nonZero OdDAI::NonPersistentList instanceUsers; pInvalidInst->findUsers(&instanceUsers); for (auto it = instanceUsers.createConstIterator(); it->next();) { OdDAIObjectSDAI* pObject; if (it->getCurrentMember() >> pObject) { OdIfcInstancePtr pInstance = OdIfcInstance::cast(pObject); OdDAIObjectId id; if (pInstance->getAttr(kPosition) >> id && id.isValid()) { OdIfcInstancePtr pPositionInst = id.openObject(); if (pPositionInst->isKindOf(kIfcAxis2Placement3D)) { if (pPositionInst->getAttr(kLocation) >> id && id.isValid()) { OdIfcInstancePtr pLocationInst = id.openObject(); //OdGePoint3d point = *OdIfcInstance::asPoint3d(pLocationInst); OdDAI::List* pointCoords; pLocationInst->getAttr(kCoordinates) >> pointCoords; double coordZ(0.); pointCoords->getByIndex(2) >> coordZ; pointCoords->putByIndex(2, coordZ + testPoint.z); } } } else { ODA_FAIL_M("Not implemented yet."); result = Logical::False; } } } } //Heal curve if (OdDAIObjectId newId = heal3dTo2d(pCurveInst)) { pInvalidInst->putAttr(kOuterCurve, newId); } } return result; }