/////////////////////////////////////////////////////////////////////////////// // 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 "OdaCommon.h" #include "OdDbConstraintsInterfacePEImpl.h" #include "DbAssocNetwork.h" #include "OdExpression.h" #include "DynamicBlocks/DbBlockConstraintParameters.h" #include "DbAssocManager.h" #include "DbDynBlockReference.h" #include "DbAssocDimDependencyBodyBase.h" #include "DbBlockParameterDependencyBody.h" #include "OdDbDynBlockAssocEvaluationCallback.h" #include "DbImpAssocDependency.h" #include "DbImpAssocAction.h" #include "DbAssoc2dConstraintGroup.h" #include "OdExplicitConstr.h" #include "OdConstrainedGeometry.h" OdDbAssocVariablePtr OdDbConstraintsInterfacePEImpl::findVariable(const OdString& name, OdDbObjectId blockId) { OdDbAssocNetworkPtr nw = OdDbAssocNetwork::getInstanceFromObject(blockId, false).openObject(); if (nw.isNull()) return OdDbAssocVariablePtr(); OdDbObjectIdArray actions = nw->getActions(); for (unsigned int i = 0; i < actions.size(); ++i) { OdDbObjectPtr action = actions[i].openObject(); if (action->isKindOf(OdDbAssocVariable::desc())) { OdDbAssocVariablePtr va = action; if (va->name() == name) return va; } } return OdDbAssocVariablePtr(); } OdResult OdDbConstraintsInterfacePEImpl::getAssocVariableName(const OdDbObject* pObject, OdString& name) { OdDbAssocVariablePtr v = OdDbAssocVariable::cast(pObject); if (v.isNull()) return eInvalidInput; name = v->name(); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::getAssocVariableValue(const OdDbObject* pObject, OdDbObjectId blockId, OdDbEvalVariant& value) { if (blockId.isNull()) { if (auto v = OdDbAssocVariable::cast(pObject)) { value = *v->value(); return eOk; } else return eInvalidInput; } OdString name; OdResult res = getAssocVariableName(pObject, name); if (res != eOk) return res; OdDbAssocVariablePtr v = findVariable(name, blockId); if (v.isNull()) return eInvalidInput; value = *v->value(); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::setAssocVariableValue(const OdDbObject* pObject, OdDbObjectId blockId, const OdDbEvalVariant& value) { if (blockId.isNull()) return eInvalidInput; OdString name; OdResult res = getAssocVariableName(pObject, name); if (res != eOk) return res; OdDbAssocVariablePtr v = findVariable(name, blockId); if (v.isNull()) return eInvalidInput; // Delegate to the simpler version return setAssocVariableValue(v, value); } OdResult OdDbConstraintsInterfacePEImpl::setAssocVariableValue(const OdDbObject* pObject, const OdDbEvalVariant& value) { if (!pObject) return eInvalidInput; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return eInvalidInput; const OdString expr = OdExpression::createExpression(OdDbEvalVariant::init(value)); pVar->upgradeOpen(); pVar->setExpression(expr, OdString::kEmpty, false, false); //Force reactors notification to dependent objects, because it doesn't happen if there is an active transaction pVar->xmitPropagateModify(); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::setAssocVariableValue(const OdString& name, OdDbObjectId blockId, const OdDbEvalVariant& value) { if (blockId.isNull() || name.isEmpty()) return eInvalidInput; // Find the variable by name OdDbAssocVariablePtr pVar = findVariable(name, blockId); if (pVar.isNull()) return eInvalidInput; // Delegate to the simpler version return setAssocVariableValue(pVar, value); } OdDbEvalVariant OdDbConstraintsInterfacePEImpl::getAssocVariableValue(const OdDbObject* pObject) { if (!pObject) return OdDbEvalVariant(); OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return OdDbEvalVariant(); return *pVar->value(); } OdString OdDbConstraintsInterfacePEImpl::getAssocVariableExpression(const OdDbObject* pObject) { if (!pObject) return OdString::kEmpty; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return OdString::kEmpty; return pVar->expression(); } OdResult OdDbConstraintsInterfacePEImpl::setAssocVariableExpression(const OdDbObject* pObject, const OdString& value) { if (!pObject) return eInvalidInput; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return eInvalidInput; pVar->upgradeOpen(); pVar->setExpression(value, OdString::kEmpty, false, false); //Force reactors notification to dependent objects, because it doesn't happen if there is an active transaction pVar->xmitPropagateModify(); return eOk; } OdString OdDbConstraintsInterfacePEImpl::getAssocVariableDescription(const OdDbObject* pObject) { if (!pObject) return OdString::kEmpty; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return OdString::kEmpty; return pVar->description(); } OdResult OdDbConstraintsInterfacePEImpl::evaluateNetwork(const OdDbObjectId& blockId, OdDbEvalContext* /*pContext*/) { // Get the object from the ID OdDbObjectPtr pObject = blockId.openObject(); if (pObject.isNull()) return eInvalidObjectId; // Get the database from the object OdDbDatabase* pDb = pObject->database(); if (!pDb) return eInvalidInput; // Call OdDbAssocManager::evaluateTopLevelNetwork bool result = OdDbAssocManager::evaluateTopLevelNetwork(pDb); return result ? eOk : eInvalidInput; } OdResult OdDbConstraintsInterfacePEImpl::setAssocVariableDescription(const OdDbObject* pObject, const OdString& description) { if (!pObject) return eInvalidInput; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return eInvalidInput; pVar->upgradeOpen(); pVar->setDescription(description); return eOk; } bool OdDbConstraintsInterfacePEImpl::isVarExpressionConstant(const OdDbObject* pAssocVar) { if (!pAssocVar) return true; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pAssocVar); if (pVar.isNull()) return true; OdString expr = pVar->expression(); if (expr.isEmpty()) return true; try { // Use OdExpression::Parser to properly determine if expression is constant OdExpression::Parser parser; OdString errorMsg; if (parser.setExpression(expr, &errorMsg) != eOk) return false; // If expression is invalid, treat as non-constant // Use the proper parser method to check if expression contains variables or random functions return parser.isConstExpression(); } catch (...) { // If parsing fails, treat as non-constant for safety return false; } } bool OdDbConstraintsInterfacePEImpl::expressionReferencesStringVar(const OdDbObject* pAssocVar, const OdString& expression) { if (!pAssocVar || expression.isEmpty()) return false; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pAssocVar); if (pVar.isNull()) return false; try { // Use OdExpression::Parser to get all variables referenced in the expression OdExpression::Parser parser; OdString errorMsg; if (parser.setExpression(expression, &errorMsg) != eOk) return false; // If expression is invalid, assume no string var references // Get list of variables used in the expression OdStringArray usedVars; if (parser.getVariables(usedVars) != eOk) return false; // Get the network that contains this variable OdDbObjectId networkId = pVar->ownerId(); if (networkId.isNull()) return false; OdDbAssocNetworkPtr pNetwork = networkId.openObject(); if (pNetwork.isNull()) return false; // Check each referenced variable to see if any are string variables for (unsigned int i = 0; i < usedVars.size(); ++i) { // Find the variable by name in the network OdDbAssocVariablePtr referencedVar = findVariable(usedVars[i], networkId); if (referencedVar.isNull()) continue; // Check if the referenced variable's value is a string type OdDbEvalVariantPtr varValue = referencedVar->value(); if (varValue && varValue->getType() == kDwgText) return true; // Found a string variable reference } return false; // No string variable references found } catch (...) { // If parsing fails, assume no string var references for safety return false; } } bool OdDbConstraintsInterfacePEImpl::isNameUniqueInNetwork(const OdDbObject* pAssocVar, const OdString& name) { if (!pAssocVar || name.isEmpty()) return true; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pAssocVar); if (pVar.isNull()) return true; // Get the network that contains this variable OdDbObjectId networkId = pVar->ownerId(); if (networkId.isNull()) return true; OdDbAssocNetworkPtr pNetwork = networkId.openObject(); if (pNetwork.isNull()) return true; // Check all actions in the network for variables with the same name OdDbObjectIdArray actions = pNetwork->getActions(); for (unsigned int i = 0; i < actions.size(); ++i) { OdDbObjectPtr action = actions[i].openObject(); if (action->isKindOf(OdDbAssocVariable::desc())) { OdDbAssocVariablePtr va = action; if (va->objectId() != pVar->objectId() && va->name() == name) return false; // Found another variable with the same name } } return true; } OdResult OdDbConstraintsInterfacePEImpl::evaluateExpression(const OdDbObject* pObject, OdDbObjectIdArray& objectIds, OdDbEvalVariantArray& objectValues, OdDbEvalVariant* evaluatedExpressionValue, OdString* errorMessage) { if (!pObject || !evaluatedExpressionValue) return eInvalidInput; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return eInvalidInput; OdArray objectValuesPtr; for (auto val : objectValues) { objectValuesPtr.append(new OdDbEvalVariant(val)); } OdDbEvalVariant value; OdResult res = pVar->evaluateExpression(objectIds, objectValuesPtr, &value, errorMessage); if (res == eOk) { evaluatedExpressionValue->setValue(OdResBuf::ValueType::kDxfReal, value.getAsDouble()); objectValues.append(*evaluatedExpressionValue); } return res; } OdResult OdDbConstraintsInterfacePEImpl::evaluateExpression(const OdDbObject* pObject, OdDbEvalVariant* evaluatedExpressionValue, OdString* errorMessage) const { if (!pObject || !evaluatedExpressionValue) return eInvalidInput; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return eInvalidInput; return pVar->evaluateExpression(evaluatedExpressionValue, errorMessage); } OdResult OdDbConstraintsInterfacePEImpl::setAssocVariableName(const OdDbObject* pObject, const OdString& name) { if (!pObject) return eInvalidInput; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return eInvalidInput; pVar->upgradeOpen(); return pVar->setName(name, true); // updateReferencingExpressions = true } OdResult OdDbConstraintsInterfacePEImpl::getDependencies(const OdDbObject* pObject, bool readDependenciesWanted, bool writeDependenciesWanted, OdDbObjectIdArray& dependencyIds) { dependencyIds.clear(); if (!pObject) return eInvalidInput; // Try to cast to OdDbAssocAction first (includes variables) if (const OdDbAssocAction* pAction = OdDbAssocAction::cast(pObject)) { return pAction->getDependencies(readDependenciesWanted, writeDependenciesWanted, dependencyIds); } return eInvalidInput; } bool OdDbConstraintsInterfacePEImpl::isAssocDependency(const OdDbObject* pObject) { if (!pObject) return false; return pObject->isKindOf(OdDbAssocDependency::desc()); } bool OdDbConstraintsInterfacePEImpl::isAssocAction(const OdDbObject* pObject) { if (!pObject) return false; return pObject->isKindOf(OdDbAssocAction::desc()); } OdResult OdDbConstraintsInterfacePEImpl::getDependencyById(const OdDbObject* pAction, OdDbObjectId templateDependencyId, OdDbObjectPtr& pRuntimeDependency) { if (!pAction || templateDependencyId.isNull()) return eInvalidInput; const OdDbAssocAction* pAssocAction = OdDbAssocAction::cast(pAction); if (!pAssocAction) return eInvalidInput; // Get all dependencies of the action OdDbObjectIdArray dependencyIds; OdResult res = pAssocAction->getDependencies(true, true, dependencyIds); if (res != eOk) return res; // Find dependency by template ID or matching criteria for (unsigned int i = 0; i < dependencyIds.size(); ++i) { OdDbObjectPtr pDep = dependencyIds[i].openObject(); if (pDep.get() && pDep->objectId() == templateDependencyId) { pRuntimeDependency = pDep; return eOk; } } return eInvalidInput; } OdDbObjectId OdDbConstraintsInterfacePEImpl::getRuntimeBlockId(const OdDbObjectId& templateBlockId) { if (templateBlockId.isNull()) return OdDbObjectId::kNull; // Check if the templateBlockId refers to a dynamic block if (OdDbDynBlockReference::isDynamicBlock(templateBlockId)) { // For dynamic blocks, we need to get the anonymous block OdDbObjectPtr pTemplateBlock = templateBlockId.openObject(); if (!pTemplateBlock.isNull()) { OdDbDynBlockTableRecord dynBlockTable(templateBlockId); if (dynBlockTable.isDynamicBlock()) { // Get all anonymous blocks for this dynamic block OdDbObjectIdArray anonymousIds; dynBlockTable.getAnonymousBlockIds(anonymousIds); // If there are anonymous blocks, use the first one for the runtime network if (!anonymousIds.isEmpty()) { return anonymousIds[0]; } } } } // For regular blocks or if no anonymous block found, return the original ID return templateBlockId; } OdResult OdDbConstraintsInterfacePEImpl::getActionFromNetwork(const OdDbObject* pOriginalAction, OdDbObjectId templateBlockId, OdDbObjectPtr& pRuntimeAction) { if (!pOriginalAction || templateBlockId.isNull()) return eInvalidInput; const OdDbAssocAction* pAssocAction = OdDbAssocAction::cast(pOriginalAction); if (!pAssocAction) return eInvalidInput; // Get the appropriate block ID to use (handles dynamic blocks) OdDbObjectId runtimeBlockId = getRuntimeBlockId(templateBlockId); if (runtimeBlockId.isNull()) return eInvalidInput; // Find the assoc network in the runtime block (could be original or anonymous) OdDbAssocNetworkPtr runtimeNetwork = OdDbAssocNetwork::getInstanceFromObject(runtimeBlockId, false).openObject(); if (runtimeNetwork.isNull()) return eInvalidInput; // Get the m_actionIndex from the original action's implementation const OdDbImpAssocAction* pOriginalImp = OdDbImpAssocAction::getImpl(pAssocAction); if (!pOriginalImp) return eInvalidInput; OdUInt32 originalIndex = pOriginalImp->getIndex(); // Get all actions from the runtime network OdDbObjectIdArray runtimeActions = runtimeNetwork->getActions(); // Find the action with matching m_actionIndex in the runtime actions for (unsigned int i = 0; i < runtimeActions.size(); ++i) { OdDbAssocActionPtr pRuntimeActionCandidate = runtimeActions[i].openObject(); if (pRuntimeActionCandidate.isNull()) continue; const OdDbImpAssocAction* pRuntimeImp = OdDbImpAssocAction::getImpl(pRuntimeActionCandidate.get()); if (pRuntimeImp && pRuntimeImp->getIndex() == originalIndex) { pRuntimeAction = pRuntimeActionCandidate; return eOk; } } return eInvalidInput; } OdResult OdDbConstraintsInterfacePEImpl::getDependencyFromNetwork(const OdDbObject* pOriginalDependency, OdDbObjectId templateBlockId, OdDbObjectId& runtimeDependencyId) { if (!pOriginalDependency || templateBlockId.isNull()) return eInvalidInput; const OdDbAssocDependency* pAssocDependency = OdDbAssocDependency::cast(pOriginalDependency); if (!pAssocDependency) return eInvalidInput; // Get the owning action for the original dependency OdDbAssocActionPtr templateAction = pAssocDependency->owningAction().openObject(); if (templateAction.isNull()) return eInvalidInput; // Get the corresponding runtime action using getActionFromNetwork OdDbObjectPtr pRuntimeActionObj; OdResult res = getActionFromNetwork(templateAction, templateBlockId, pRuntimeActionObj); if (res != eOk) return res; OdDbAssocActionPtr runtimeAction = OdDbAssocAction::cast(pRuntimeActionObj); if (runtimeAction.isNull()) return eInvalidInput; // Get all dependencies of the runtime action OdDbObjectIdArray runtimeDependencyIds; res = runtimeAction->getDependencies(true, true, runtimeDependencyIds); if (res != eOk) return res; // Get the m_nIndex from the original dependency's implementation const OdDbImpAssocDependency* pOriginalImp = OdDbImpAssocDependency::getImpl(pAssocDependency); if (!pOriginalImp) return eInvalidInput; OdUInt32 originalIndex = pOriginalImp->getIndex(); // Find the dependency with matching m_nIndex in the runtime dependencies for (unsigned int i = 0; i < runtimeDependencyIds.size(); ++i) { OdDbAssocDependencyPtr pRuntimeDep = runtimeDependencyIds[i].openObject(); if (pRuntimeDep.isNull()) continue; const OdDbImpAssocDependency* pRuntimeImp = OdDbImpAssocDependency::getImpl(pRuntimeDep.get()); if (pRuntimeImp && pRuntimeImp->getIndex() == originalIndex) { runtimeDependencyId = pRuntimeDep->objectId(); return eOk; } } return eInvalidInput; } OdResult OdDbConstraintsInterfacePEImpl::getVariableNameAndExpression(const OdDbObject* pObject, OdString& name, OdString& expression) { name.empty(); expression.empty(); if (!pObject) return eInvalidInput; const OdDbAssocDependency* pDep = OdDbAssocDependency::cast(pObject); if (!pDep) return eInvalidInput; // Get the associated variable using existing logic OdDbObjectId varId; OdResult res = getAssocVariableFromDep(pDep, varId); if (res != eOk) return res; // Open the variable and get its name and expression OdDbAssocVariablePtr pVar = varId.openObject(); if (pVar.isNull()) return eNullObjectId; name = pVar->name(); expression = pVar->expression(); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::dependentOnObject(const OdDbObject* pObject, OdDbObjectId& dependentOnObjectId) { if (!pObject) return eInvalidInput; const OdDbAssocDependency* pDep = OdDbAssocDependency::cast(pObject); if (!pDep) return eInvalidInput; dependentOnObjectId = pDep->dependentOnObject(); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::getAssocVariable(const OdDbObject* pObject, OdDbObjectId& varId) { if (!pObject) return eInvalidInput; OdDbAssocDependency* pDep = OdDbAssocDependency::cast(pObject); if (pDep) return getAssocVariableFromDep(pDep, varId); return eNotImplemented; } OdResult OdDbConstraintsInterfacePEImpl::getAssocVariable(const OdDbObject* pObject, OdDbObjectId blockId, OdDbObjectId& varId) { if (blockId.isNull()) { // If blockId is null, delegate to the simpler version return getAssocVariable(pObject, varId); } OdString name; OdResult res = getAssocVariableName(pObject, name); if (res != eOk) return res; OdDbAssocVariablePtr v = findVariable(name, blockId); if (v.isNull()) return eInvalidInput; varId = v->objectId(); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::getAssocVariableFromDep(const OdDbAssocDependency* pDep, OdDbObjectId& varId) { // First, try to get the variable directly from the dependency body OdDbObjectId dependencyBodyId = pDep->dependencyBody(); if (!dependencyBodyId.isNull()) { OdDbObjectPtr pDependencyBody = dependencyBodyId.openObject(); if (!pDependencyBody.isNull()) { // Try to cast to OdDbAssocDimDependencyBodyBase which has variable() method if (auto dimDepBody = OdDbAssocDimDependencyBodyBase::cast(pDependencyBody)) { OdDbObjectId varIdFromBody = dimDepBody->variable(); if (!varIdFromBody.isNull()) { varId = varIdFromBody; return eOk; } } } } // Fallback to the original logic if dependency body doesn't provide variable OdDbObjectPtr dependentOnObj = pDep->dependentOnObject().openObject(); if (dependentOnObj.isNull()) return eNullObjectId; if (dependentOnObj->isKindOf(OdDbAssocVariable::desc())) { varId = pDep->dependentOnObject(); return eOk; } if (auto constraintParam = OdDbBlockConstraintParameter::cast(dependentOnObj)) { OdDbAssocActionPtr pAction = pDep->owningAction().openObject(); if (pAction.isNull()) return eNullObjectId; OdDbAssocNetworkPtr pNetwork = pAction->ownerId().openObject(); if (pNetwork.isNull()) return eNullObjectId; OdString paramName = constraintParam->distanceName(); if (!paramName.isEmpty()) { OdDbObjectIdArray actions = pNetwork->getActions(); for (unsigned int i = 0; i < actions.size(); ++i) { OdDbObjectPtr action = actions[i].openObject(); if (action->isKindOf(OdDbAssocVariable::desc())) { OdDbAssocVariablePtr va = action; if (va->name() == paramName) { varId = va->objectId(); return eOk; } } } } } return eInvalidInput; } OdResult OdDbConstraintsInterfacePEImpl::isAssocVariableDependent(const OdDbObject* pObject, bool& bDependent) { if (!pObject) return eInvalidInput; OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pObject); if (pVar.isNull()) return eInvalidInput; bDependent = false; // Check if the variable has any dependencies (read or write) OdDbObjectIdArray dependencyIds; if (pVar->getDependencies(true, true, dependencyIds) == eOk) { // If the variable has any dependencies, it's considered dependent bDependent = !dependencyIds.isEmpty(); } return eOk; } bool OdDbConstraintsInterfacePEImpl::isAssocVariable(const OdDbObject* pObject) { if (!pObject) return false; // Check if the object is an OdDbAssocVariable return pObject->isKindOf(OdDbAssocVariable::desc()); } OdResult OdDbConstraintsInterfacePEImpl::setVariableNameAndExpression(OdDbObject* pDependency, const OdString& name, const OdString& expression) { if (!pDependency) return eInvalidInput; OdDbAssocDependency* pDep = OdDbAssocDependency::cast(pDependency); if (!pDep) return eInvalidInput; // Get the dependency body OdDbObjectId dependencyBodyId = pDep->dependencyBody(); if (dependencyBodyId.isNull()) return eInvalidInput; OdDbObjectPtr pDependencyBody = dependencyBodyId.openObject(OdDb::kForWrite); if (pDependencyBody.isNull()) return eInvalidInput; // Try to cast to OdDbAssocDimDependencyBodyBase which has setVariableNameAndExpression method OdDbAssocDimDependencyBodyBase* pDimDepBody = OdDbAssocDimDependencyBodyBase::cast(pDependencyBody); if (pDimDepBody) { return pDimDepBody->setVariableNameAndExpression(name, expression); } return eNotImplemented; } OdResult OdDbConstraintsInterfacePEImpl::evaluate(OdDbObject* pDependency) { if (!pDependency) return eInvalidInput; OdDbAssocDependency* pDep = OdDbAssocDependency::cast(pDependency); if (!pDep) return eInvalidInput; // Upgrade to write mode if needed if (!pDep->isWriteEnabled()) pDep->upgradeOpen(); // Call the dependency's evaluate method pDep->evaluate(); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::addBlockPostponedAction(OdDbBlockPostponedActionPtr pAction) { if (pAction.isNull()) return eInvalidInput; // Check if global callback is initialized if (!g_pDynBlockAssocCallback) return eNotInitializedYet; // Add postponed action to the global callback g_pDynBlockAssocCallback->addPostponedAction(pAction); return eOk; } OdResult OdDbConstraintsInterfacePEImpl::setChangedStatus(const OdDbObjectId& dependencyId) { if (dependencyId.isNull()) return eInvalidInput; // Open the dependency for write OdDbAssocDependencyPtr pDep = OdDbAssocDependency::cast(dependencyId.openObject(OdDb::kForWrite)); if (pDep.isNull()) return eInvalidInput; // Set the dependency status to changed directly return pDep->setStatus(kChangedDirectlyAssocStatus); } OdResult OdDbConstraintsInterfacePEImpl::getOwningNetwork(const OdDbObject* pObject, OdDbObjectId& networkId) { if (!pObject) return eInvalidInput; // For AssocVariable and AssocAction, get owning network if (OdDbAssocActionPtr pAction = OdDbAssocAction::cast(pObject)) { networkId = pAction->owningNetwork(); return networkId.isNull() ? eNullObjectId : eOk; } // For AssocDependency, get owning network through owning action if (OdDbAssocDependencyPtr pDependency = OdDbAssocDependency::cast(pObject)) { // Get the owning action of the dependency OdDbObjectId owningActionId = pDependency->owningAction(); if (owningActionId.isNull()) return eNullObjectId; // Open the owning action OdDbAssocActionPtr pOwningAction = owningActionId.openObject(); if (pOwningAction.isNull()) return eNullObjectId; // Get the network from the action networkId = pOwningAction->owningNetwork(); return networkId.isNull() ? eNullObjectId : eOk; } return eNotApplicable; } OdResult OdDbConstraintsInterfacePEImpl::getInstanceFromObject(const OdDbObject* pObject, OdDbObjectId& networkId, bool createIfDoesntExist) { if (!pObject) return eInvalidInput; // Get network ID from block table record or other object OdDbObjectId objectId = pObject->objectId(); if (objectId.isNull()) return eInvalidInput; networkId = OdDbAssocNetwork::getInstanceFromObject(objectId, createIfDoesntExist); return networkId.isNull() ? eNotInDatabase : eOk; } OdResult OdDbConstraintsInterfacePEImpl::getNetworkActions(const OdDbObjectId& networkId, OdDbObjectIdArray& varIds) { if (networkId.isNull()) return eInvalidInput; // Open the network OdDbAssocNetworkPtr pNetwork = OdDbAssocNetwork::cast(networkId.openObject()); if (pNetwork.isNull()) return eInvalidInput; // Get all actions from the network OdDbObjectIdArray actions = pNetwork->getActions(); // Filter only AssocVariable objects varIds.clear(); for (unsigned int i = 0; i < actions.size(); ++i) { OdDbObjectPtr pAction = actions[i].openObject(); if (!pAction.isNull()) { varIds.append(actions[i]); } } return eOk; } OdResult OdDbConstraintsInterfacePEImpl::getDependencyBody(const OdDbObject* pDependency, OdDbObjectId& bodyId) { if (!pDependency) return eInvalidInput; OdDbAssocDependencyPtr pDep = OdDbAssocDependency::cast(pDependency); if (pDep.isNull()) return eInvalidInput; bodyId = pDep->dependencyBody(); return bodyId.isNull() ? eNullObjectId : eOk; } OdResult OdDbConstraintsInterfacePEImpl::setOwningBlockReference(const OdDbObject* pAssocAction, const OdDbObjectId& blockRefId) { if (!pAssocAction || blockRefId.isNull()) return eInvalidInput; // Get all dependencies from the variable (read and write) OdDbObjectIdArray dependencyIds; OdResult res = getDependencies(pAssocAction, true, true, dependencyIds); if (res != eOk) return res; // Process each dependency for (unsigned int i = 0; i < dependencyIds.size(); ++i) { // Open the dependency OdDbObjectPtr pDep = dependencyIds[i].openObject(); if (pDep.isNull()) continue; // Get dependency body OdDbObjectId bodyId; res = getDependencyBody(pDep, bodyId); if (res != eOk || bodyId.isNull()) continue; // Open the body for write OdDbObjectPtr pBody = bodyId.openObject(OdDb::kForWrite); if (pBody.isNull()) continue; // Check if it's a block parameter dependency body OdDbBlockParameterDependencyBodyPtr pBlockDepBody = OdDbBlockParameterDependencyBody::cast(pBody); if (!pBlockDepBody.isNull()) { // Set the owning block reference ID pBlockDepBody->m_owningBlockRefId = blockRefId; } } return eOk; } OdResult OdDbConstraintsInterfacePEImpl::getAngularConstraintSegmentType(const OdDbObject* pAssocVar, bool& bCCW, bool& bAntiparallel) { if (!pAssocVar) return eInvalidInput; // Cast to OdDbAssocVariable OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pAssocVar); if (pVar.isNull()) return eInvalidInput; // Get the owning network OdDbObjectId networkId = pVar->ownerId(); if (networkId.isNull()) return eInvalidInput; OdDbAssocNetworkPtr pNetwork = networkId.openObject(); if (pNetwork.isNull()) return eInvalidInput; // Find the constraint group that contains this variable OdDbObjectIdArray actions = pNetwork->getActions(); for (unsigned int i = 0; i < actions.size(); ++i) { OdDbObjectPtr pAction = actions[i].openObject(); if (!pAction.isNull() && pAction->isKindOf(OdDbAssoc2dConstraintGroup::desc())) { OdDbAssoc2dConstraintGroupPtr pGroup = pAction; // Get all constraints from the group OdArray constraints; pGroup->getConstraints(constraints); // Find angle constraint that references this variable for (unsigned int j = 0; j < constraints.size(); ++j) { if (constraints[j]->isKindOf(OdAngleConstraint::desc())) { OdAngleConstraint* pAngleConstraint = static_cast(constraints[j]); // Check if this constraint uses our variable OdDbObjectId dimDepId = pAngleConstraint->dimDependencyId(); if (!dimDepId.isNull()) { OdDbAssocDependencyPtr pDep = dimDepId.openObject(); if (!pDep.isNull()) { OdDbObjectId bodyId = pDep->dependencyBody(); if (!bodyId.isNull()) { OdDbAssocDimDependencyBodyBasePtr pBody = bodyId.openObject(); if (!pBody.isNull() && pBody->variable() == pVar->objectId()) { // Found the constraint that uses this variable OdAngleConstraint::SectorType sectorType = pAngleConstraint->sectorType(); // Determine CCW and antiparallel based on sector type switch (sectorType) { case OdAngleConstraint::kParallelAntiClockwise: bCCW = true; bAntiparallel = false; break; case OdAngleConstraint::kAntiParallelClockwise: bCCW = false; bAntiparallel = true; break; case OdAngleConstraint::kParallelClockwise: bCCW = false; bAntiparallel = false; break; case OdAngleConstraint::kAntiParallelAntiClockwise: bCCW = true; bAntiparallel = true; break; default: bCCW = true; bAntiparallel = false; break; } return eOk; } } } } } } } } // Default values if constraint not found bCCW = true; bAntiparallel = false; return eInvalidInput; } OdResult OdDbConstraintsInterfacePEImpl::getAngularConstraintRefEnt(const OdDbObject* pAssocVar, const OdDbObjectId& blockId, OdDbObjectId& refEntId) { if (!pAssocVar || blockId.isNull()) return eInvalidInput; // Cast to OdDbAssocVariable OdDbAssocVariablePtr pVar = OdDbAssocVariable::cast(pAssocVar); if (pVar.isNull()) return eInvalidInput; // Get the network for the specified block ID // This is important for dynamic blocks where we need to find the constraint in the specific block instance OdDbAssocNetworkPtr pNetwork = OdDbAssocNetwork::getInstanceFromObject(blockId, false).openObject(); if (pNetwork.isNull()) return eInvalidInput; // Find the variable with the same name in this network OdString varName = pVar->name(); if (varName.isEmpty()) return eInvalidInput; OdDbAssocVariablePtr pBlockVar = findVariable(varName, blockId); if (pBlockVar.isNull()) return eInvalidInput; // Find the constraint group that contains this variable OdDbObjectIdArray actions = pNetwork->getActions(); for (unsigned int i = 0; i < actions.size(); ++i) { OdDbObjectPtr pAction = actions[i].openObject(); if (!pAction.isNull() && pAction->isKindOf(OdDbAssoc2dConstraintGroup::desc())) { OdDbAssoc2dConstraintGroupPtr pGroup = pAction; // Get all constraints from the group OdArray constraints; pGroup->getConstraints(constraints); // Find angle constraint that references this variable for (unsigned int j = 0; j < constraints.size(); ++j) { if (constraints[j]->isKindOf(OdAngleConstraint::desc())) { OdAngleConstraint* pAngleConstraint = static_cast(constraints[j]); // Check if this constraint uses our variable OdDbObjectId dimDepId = pAngleConstraint->dimDependencyId(); if (!dimDepId.isNull()) { OdDbAssocDependencyPtr pDep = dimDepId.openObject(); if (!pDep.isNull()) { OdDbObjectId bodyId = pDep->dependencyBody(); if (!bodyId.isNull()) { OdDbAssocDimDependencyBodyBasePtr pBody = bodyId.openObject(); if (!pBody.isNull() && pBody->variable() == pBlockVar->objectId()) { // Found the constraint that uses this variable // Get the constrained geometries OdArray geoms; pAngleConstraint->getConnectedGeometries(geoms); // Get the first geometry's entity ID as the reference entity // This entity will be the one that needs to be rotated if (geoms.size() > 0 && geoms[0]) { OdArray paths; geoms[0]->getFullSubentPaths(paths); if (paths.size() > 0) { const OdDbObjectIdArray& objIds = paths[0].objectIds(); if (objIds.size() > 0) { refEntId = objIds[objIds.size() - 1]; return eOk; } } } } } } } } } } } refEntId = OdDbObjectId::kNull; return eInvalidInput; } OdResult OdDbConstraintsInterfacePEImpl::getAngularConstraintType(const OdDbObjectId& dependencyId, int& constraintType) { // Default value (kParallelAntiClockwise = 0 -> maps to type 4) constraintType = 4; if (dependencyId.isNull()) return eNullObjectId; // Open the dependency object OdDbAssocDependencyPtr pDependency = dependencyId.openObject(); if (pDependency.isNull()) return eNullObjectId; // Get dependency body ID OdDbObjectId bodyId; OdResult res = getDependencyBody(pDependency, bodyId); if (res != eOk || bodyId.isNull()) return res; // Open dependency body OdDbAssocDimDependencyBodyBasePtr pBody = bodyId.openObject(); if (pBody.isNull()) return eNullObjectId; // Get the constraint directly from the body OdExplicitConstraint* pConstraint = pBody->constraint(); if (!pConstraint) return eNullObjectId; // Try to cast to OdAngleConstraint if (!pConstraint->isKindOf(OdAngleConstraint::desc())) return eNotApplicable; OdAngleConstraint* pAngleConstraint = static_cast(pConstraint); // Get sector type and map to constraint type OdAngleConstraint::SectorType sectorType = pAngleConstraint->sectorType(); // Map sector type to constraint type switch (sectorType) { case OdAngleConstraint::kParallelAntiClockwise: // 0 constraintType = 4; // kAngular3Point break; case OdAngleConstraint::kAntiParallelClockwise: // 1 constraintType = 5; // kAngular4Point break; case OdAngleConstraint::kParallelClockwise: // 2 constraintType = 6; // kAngularArc break; case OdAngleConstraint::kAntiParallelAntiClockwise: // 3 constraintType = 7; // kAngular2Line break; default: constraintType = 4; break; } return eOk; }