/////////////////////////////////////////////////////////////////////////////// // 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 "IfcCompositeCurveConsistencyValidationTask.h" #include "daiValidationCommon.h" #include "IfcEntity.h" #include "IfcCurveSegment.h" #include "IfcFile.h" #define BASE_COMPOSITE_CURVE_NAME "IfcCompositeCurve" #define DEFAULT_TOLERANCE OdGeTol(1.e-6) #define DISCONTINUOUS_ERROR "DISCONTINUOUS transition is permitted only at the last curve segment" #define SEGMENTS_BORDER_ERROR "Segments end and start points are not equal" #define SEGMENTS_TANGENTS_ERROR "Tangents at the segments end and start points are not codirectional" #define NONDISCONTINUOUS_AT_THE_END_WARNING "Warning: non-DISCONTINUOUS transition at the end point indicates that the curve is closed" using namespace OdIfc; using namespace OdDAI; ODRX_VALIDATION_CONS_DEFINE_MEMBERS(CompositeCurveConsistencyValidationTask, ExtentValidationTask, RXIMPL_CONSTR); /// /// TO DO: Add equality of curvatures at extreme points for CONTSAMEGRADIENTSAMECURVATURE. Need curvature in point /// TO DO: validation of the last segment. Need drawing closed curves /// namespace { struct SegmentExtremePoints { OdIfcInstancePtr pSegmentEntity; OdGePoint3d startPoint; OdGePoint3d endPoint; OdGeVector3d startTangent; OdGeVector3d endTangent; }; void unresolveEntity(OdIfcInstancePtr pEntity); void checkUnresolveAttrs(OdIfcInstancePtr pEntity, Entity* pInstance) { const AttributeSet& attributeSet = (pInstance)->attributes(); if (attributeSet.getMemberCount() > 0) { const OdArray& attributes = attributeSet.getArray(); for (OdArray::const_iterator it = attributes.begin(); it < attributes.end(); ++it) { if ((*it)->getAttributeType() != OdDAI::AttributeType::Explicit) { continue; } ExplicitAttributePtr explicitAttr = ExplicitAttribute::cast(*it); if (!explicitAttr.isNull()) { BaseTypePtr baseType = explicitAttr->domain(); if (!pEntity->testAttr(explicitAttr->name())) continue; Select* select = nullptr; if (baseType->isNamedType()) { NamedTypePtr namedType = baseType->namedType(); EntityPtr entityCheck = Entity::cast(namedType); if (!entityCheck.isNull()) { OdDAIObjectId id; if (pEntity->getAttr(explicitAttr->name()) >> id) { unresolveEntity(id.openObject()); continue; } } DefinedTypePtr defType = DefinedType::cast(namedType); if (!defType.isNull()) { UnderlyingTypePtr underlyingType = defType->domain(); if (underlyingType->isConstructedType()) { ConstructedTypePtr constructedType = underlyingType->constructedType(); if (constructedType->isSelectType()) { SelectTypePtr selectType = constructedType->selectType(); OdDAIObjectId id; if (pEntity->getAttr(explicitAttr->name()) >> id) { unresolveEntity(id.openObject()); continue; } } } } } else if (baseType->isAggregationType()) { AggregationTypePtr aggregationType = baseType->aggregationType(); BaseTypePtr baseAggrType = aggregationType->elementType(); if (baseAggrType->isNamedType()) { NamedTypePtr namedType = baseAggrType->namedType(); EntityPtr entityCheck = Entity::cast(namedType); if (!entityCheck.isNull()) { OdDAIObjectIds ids; if (pEntity->getAttr(explicitAttr->name()) >> ids) { for (const auto& id : ids) unresolveEntity(id.openObject()); continue; } } } else if (baseAggrType->isAggregationType()) { ODA_ASSERT_ONCE(!"Unresolving of AggregationTypes inside AggregationType is unsupporting yet"); continue; } } } } } } void unresolveEntity(OdIfcInstancePtr pEntity) { pEntity->unresolve(); Entity* pInstance = pEntity->getInstanceType(); checkUnresolveAttrs(pEntity, pInstance); const OdArray& supertypes = pInstance->supertypes().getArray(); for (OdArray::const_iterator it = supertypes.begin(); it < supertypes.end(); ++it) { checkUnresolveAttrs(pEntity , *it); } } bool getCurveData(OdDAIObjectId* id, OdIfcFile* file, SegmentExtremePoints* segment) { segment->pSegmentEntity = id->openObject(); if (segment->pSegmentEntity.isNull()) return false; bool isAlreadyResolved = false; if (segment->pSegmentEntity->resolved() == kUnresolved) file->get(*id); else isAlreadyResolved = true; OdIfc::OdIfcCompoundPtr compound = OdIfcInstance::asCompound(segment->pSegmentEntity); if (compound.isNull()) { ODA_ASSERT_ONCE(!compound.isNull()); return false; } OdIfc::OdIfcSegmentPtr curveSegment = OdIfcSegment::cast(compound); if (curveSegment.isNull()) { ODA_ASSERT_ONCE(!curveSegment.isNull()); return false; } const OdGeCurve3d* geCurve = curveSegment->getGeCurve(); OdGeInterval interval; geCurve->getInterval(interval); OdGeVector3dArray tangents; segment->startPoint = geCurve->evalPoint(interval.lowerBound(), 1, tangents); segment->startTangent = tangents.first(); tangents.clear(); segment->endPoint = geCurve->evalPoint(interval.upperBound(), 1, tangents); segment->endTangent = tangents.first(); // ODA_ASSERT_ONCE(geCurve->hasStartPoint(startPoint)); // ODA_ASSERT_ONCE(geCurve->hasEndPoint(endPoint)); if (!isAlreadyResolved) { unresolveEntity(segment->pSegmentEntity); // segment->pSegmentEntity->unresolve(); } return true; } } OdIfc::CompositeCurveConsistencyValidationTask::CompositeCurveConsistencyValidationTask() { m_extentName = BASE_COMPOSITE_CURVE_NAME; } OdDAI::Logical CompositeCurveConsistencyValidationTask::validate(OdDAI::InstanceValidationContext* instanceCtx, OdSharedPtr& invalidParams) { InvalidRxArrayValidationParams* invalidInstances = new InvalidRxArrayValidationParams(); invalidParams = invalidInstances; OdDAI::InstanceValidationContext* pIfcCtx = dynamic_cast(instanceCtx); if (pIfcCtx == nullptr) { return Logical::False; } OdDAIObjectIds segmentsIds; if (!(pIfcCtx->pInstance->getAttr("segments") >> segmentsIds)) { ODA_ASSERT_ONCE(!"Error while processing segments ids!"); return Logical::False; } if (segmentsIds.size() > 1) { OdSharedPtr pSegmentFirst = new SegmentExtremePoints(); if (!getCurveData(segmentsIds.begin(), OdIfcFile::cast(pIfcCtx->pFile), pSegmentFirst)) { ODA_ASSERT_ONCE(!"Error while processing segments data!"); return Logical::False; } for (auto seg = segmentsIds.begin() + 1; seg != segmentsIds.end(); ++seg) { ApplicationInstancePtr secondSeg = seg->getNested(); OdAnsiString transition; if(!(pSegmentFirst->pSegmentEntity->getAttr("transition")>> transition)) { ODA_ASSERT_ONCE(!"Error while processing transition!"); } else if (transition == "DISCONTINUOUS") { if (!getCurveData(seg, OdIfcFile::cast(pIfcCtx->pFile), pSegmentFirst)) { ODA_ASSERT_ONCE(!"Error while processing segments data!"); break; } invalidInstances->addData(InvalidRxObjectsValidationParams({ seg->getNested() }, DISCONTINUOUS_ERROR, Logical::False)); continue; } OdSharedPtr pSegmentSecond = new SegmentExtremePoints(); if (!getCurveData(seg, OdIfcFile::cast(pIfcCtx->pFile), pSegmentSecond)) { ODA_ASSERT_ONCE(!"Error while processing segments data!"); break; } if (!pSegmentFirst->endPoint.isEqualTo(pSegmentSecond->startPoint, DEFAULT_TOLERANCE)) { invalidInstances->addData(InvalidRxObjectsValidationParams({ (seg - 1)->getNested(), seg->getNested() }, SEGMENTS_BORDER_ERROR, Logical::False)); } if (transition != "CONTINUOUS") { if (!pSegmentFirst->endTangent.isCodirectionalTo(pSegmentSecond->startTangent, DEFAULT_TOLERANCE)) { invalidInstances->addData(InvalidRxObjectsValidationParams({ (seg - 1)->getNested(), seg->getNested() }, SEGMENTS_TANGENTS_ERROR, Logical::False)); } } pSegmentFirst = pSegmentSecond; } OdAnsiString transition; if (!(pSegmentFirst->pSegmentEntity->getAttr("transition") >> transition)) { ODA_ASSERT_ONCE(!"Error while processing transition!"); } else if (transition != "DISCONTINUOUS") { invalidInstances->addData(InvalidRxObjectsValidationParams({ segmentsIds.last().getNested() }, NONDISCONTINUOUS_AT_THE_END_WARNING, Logical::Unknown)); if (invalidInstances->invalidItemsCount() == 1) return Logical::Unknown; } } else if (segmentsIds.size() == 1) { ApplicationInstancePtr pSegment = segmentsIds.begin()->openObject(); OdAnsiString transition; if (!(pSegment->getAttr("transition") >> transition)) { ODA_ASSERT_ONCE(!"Error while processing transition!"); } else if (transition != "DISCONTINUOUS") { invalidInstances->addData(InvalidRxObjectsValidationParams({ segmentsIds.last().getNested() }, NONDISCONTINUOUS_AT_THE_END_WARNING, Logical::Unknown)); if (invalidInstances->invalidItemsCount() == 1) return Logical::Unknown; } } if (invalidInstances->invalidItemsCount() > 0) return Logical::False; return Logical::True; } OdAnsiString CompositeCurveConsistencyValidationTask::description() const { return "Composite curve segments consistency validation"; }