/////////////////////////////////////////////////////////////////////////////// // 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 "DWGConstraintCreation.h" #include "Entities.h" #include "DWGCommandsUtils.h" #include "OdConstrainedGeometry.h" #include "OdDbAssocCurvePersSubentIdPE.h" #include "DWGConstraintArgsValidator.h" #define STL_USING_MEMORY #include "OdaSTL.h" #include "Si/SiSpatialIndex.h" #include "OdAutoPtr.h" #include "CsConstraint.h" #include "CsConstraintSystem.h" #include "../Source/CsValidator.h" #include "CsOverDefinedChecker.h" #include "../Source/CsFlowNetwork.h" #include "../Source/CsFlowNetworkDotTracer.h" #include "ConstraintSolver.h" void DWGConstraintsTrace(const OdString& iMsg); static OdResult createLinesConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2, const OdGeomConstraint::GeomConstraintType& constraintType) { OdResult es = eOk; OdDbFullSubentPathArray aPaths; OdDbFullSubentPath subentPath1; LinesConstraintArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); ODA_VERIFY_ONCE(isValidCrv1 && isValidCrv2); if (DWGConstraintUtils::isText(entId1) || DWGConstraintUtils::isEllipse(entId1)) { if ((es = DWGConstraintUtils::getClosestAxis(entId1, ptEnt1, subentPath1)) != eOk) return es; } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, subentPath1)) != eOk) return es; } aPaths.append(subentPath1); OdDbFullSubentPath subentPath2, pointEntPath2, edgeEntPathBeg2, edgeEntPathEnd2; if (DWGConstraintUtils::isText(entId2) || DWGConstraintUtils::isEllipse(entId2)) { if ((es = DWGConstraintUtils::getClosestAxis(entId2, ptEnt2, subentPath2)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, pointEntPath2)) != eOk) return es; } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, subentPath2, NULL, NULL, &pointEntPath2, &edgeEntPathBeg2, &edgeEntPathEnd2)) != eOk) return es; } aPaths.append(subentPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPaths, pConsGeoms)) != eOk) { return es; } DWGConstraintUtils::DwgConstraintEvaluationCallback callback; // hint fixed geometry DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); DWGConstraintUtils::addHintGeometryFixation(pDatabase, pointEntPath2, callback); // fix second seg len DWGConstraintUtils::addHintDistanceFixation(pDatabase, edgeEntPathBeg2, edgeEntPathEnd2, callback); es = DWGConstraintUtils::addGeomConstraint(pDatabase, constraintType, aPaths, &callback); return es; } OdResult DwgConstraintCreation::createParallelConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; return createLinesConstraint(pDatabase, entId1, entId2, ptEnt1, ptEnt2, OdGeomConstraint::kParallel); } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createPerpendicularConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; return createLinesConstraint(pDatabase, entId1, entId2, ptEnt1, ptEnt2, OdGeomConstraint::kPerpendicular); } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createTangentConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; OdResult es = eOk; OdGe::EntityId type1 = DWGConstraintUtils::entityType(entId1, ptEnt1); OdGe::EntityId type2 = DWGConstraintUtils::entityType(entId2, ptEnt2); (void)type2; TangentArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); if (!isValidCrv1 || !isValidCrv2) { return eInvalidInput; } const bool bArcFirst = type1 == OdGe::kCircArc3d || type1 == OdGe::kEllipArc3d; OdDbFullSubentPathArray aEdgePaths; OdDbFullSubentPath edgeEntPath1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1)) != eOk) { return es; } aEdgePaths.append(edgeEntPath1); OdDbFullSubentPath edgeEntPath2, edgeEntPathBeg2, edgeEntPathEnd2; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, NULL, NULL, NULL, &edgeEntPathBeg2, &edgeEntPathEnd2)) != eOk) { return es; } aEdgePaths.append(edgeEntPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aEdgePaths, pConsGeoms)) != eOk) { return es; } // hint fixed geometry OdGroupNodeIdArray fixedGeom; fixedGeom.append(pConsGeoms[0]->nodeId()); DWGConstraintUtils::DwgConstraintEvaluationCallback callback; if (bArcFirst) { // will be added in createTangentAuxiliaryConstraints /*// fix line angle OdConstrainedGeometry* pPointGeomBeg = NULL; OdConstrainedGeometry* pPointGeomEnd = NULL; if (DWGConstraintUtils::getConstrainedGeometry(pDatabase, edgeEntPathBeg2, pPointGeomBeg) == eOk && DWGConstraintUtils::getConstrainedGeometry(pDatabase, edgeEntPathEnd2, pPointGeomEnd) == eOk) { DWGConstraintUtils::AngleFixation& angleFixation = *callback._fixedAngle.append(); angleFixation.lineToFixAngle = pConsGeoms[1]->nodeId(); }*/ } else { OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); OdConstrainedGeometry* pPointGeomCenter = NULL; if (DWGConstraintUtils::getConstrainedGeometry(pDatabase, centerPath2, pPointGeomCenter) == eOk) { // fix arc radius DWGConstraintUtils::ArcFixation& arcFixation = *callback._fixedRadius.append(); arcFixation.arc = pConsGeoms[1]->nodeId(); arcFixation.center = pPointGeomCenter->nodeId(); // fix arc start/end angles #if 0 OdDbFullSubentPath ptBegPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kStart)); OdDbFullSubentPath ptEndPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kEnd)); OdConstrainedGeometry* pPointGeomBeg = NULL; OdConstrainedGeometry* pPointGeomEnd = NULL; if (DWGConstraintUtils::getConstrainedGeometry(pDatabase, ptBegPath2, pPointGeomBeg) == eOk && DWGConstraintUtils::getConstrainedGeometry(pDatabase, ptEndPath2, pPointGeomEnd) == eOk) { DWGConstraintUtils::AngleFixation& angleFixationBeg = *callback._fixedAngle.append(); angleFixationBeg.fixedAxisHint = pConsGeoms[0]->nodeId(); angleFixationBeg.pt1 = pPointGeomCenter->nodeId(); angleFixationBeg.pt2 = pPointGeomBeg->nodeId(); DWGConstraintUtils::AngleFixation& angleFixationEnd = *callback._fixedAngle.append(); angleFixationEnd.fixedAxisHint = pConsGeoms[0]->nodeId(); angleFixationEnd.pt1 = pPointGeomCenter->nodeId(); angleFixationEnd.pt2 = pPointGeomEnd->nodeId(); } #endif } } callback._fixedGeom.append(fixedGeom); es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kTangent, aEdgePaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createSmoothConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; OdResult es = eOk; OdGe::EntityId type1 = DWGConstraintUtils::entityType(entId1, ptEnt1); if (type1 != OdGe::kNurbCurve3d) { return eInvalidInput; } OdGe::EntityId type2 = DWGConstraintUtils::entityType(entId2, ptEnt2); if (type2 != OdGe::kNurbCurve3d && type2 != OdGe::kCircArc3d && type2 != OdGe::kLineSeg3d && type2 != OdGe::kRay3d) { return eInvalidInput; } OdDbFullSubentPathArray aEdgePaths, aPointPaths; OdDbFullSubentPath edgeEntPath1, edgePointPath1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, nullptr, nullptr, &edgePointPath1)) != eOk) { return es; } aEdgePaths.append(edgeEntPath1); aPointPaths.append(edgePointPath1); OdDbFullSubentPath edgeEntPath2, edgePointPath2; OdDbFullSubentPathArray pathsRequiresMidPoint; if (type2 != OdGe::kCircArc3d) { bool addMidPoint = false; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, addMidPoint, nullptr, nullptr, &edgePointPath2)) != eOk) { return es; } if (addMidPoint) { pathsRequiresMidPoint.append(edgeEntPath2); } } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, 0, 0, &edgePointPath2, 0, 0, 0)) != eOk) { return es; } } aEdgePaths.append(edgeEntPath2); aPointPaths.append(edgePointPath2); OdArray pConsGeoms; if (type2 != OdGe::kCircArc3d) { if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aEdgePaths, pathsRequiresMidPoint, pConsGeoms)) != eOk) { return es; } } else { if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aEdgePaths, pConsGeoms)) != eOk) { return es; } } es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kG2Smooth, aPointPaths, nullptr); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult createSymmetricConstraint_TwoEntities(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, // ObjectID of first entity const OdDbObjectId& entId2, // ObjectID of second entity const OdDbObjectId& entSymId, // ObjectID of third entity const OdGePoint3d& ptEnt1, // Point on first entity used to determine which edge subentity is closest to it const OdGePoint3d& ptEnt2, // Point on second entity used to determine which edge subentity is closest to it const OdGePoint3d& ptEntSym) // Point on third entity used to determine which edge subentity is closest to it { OdDbFullSubentPathArray aEdgePaths; OdResult es = eOk; SymmetricArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); bool isValidCrv3 = argsValidator.isEntityValidForConstraint(entSymId, ptEntSym); if (!isValidCrv1 || !isValidCrv2 || !isValidCrv3) { return eInvalidInput; } OdDbFullSubentPath edgeEntPath1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1)) != eOk) return es; aEdgePaths.append(edgeEntPath1); OdDbFullSubentPath edgeEntPath2; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2)) != eOk) return es; aEdgePaths.append(edgeEntPath2); OdDbFullSubentPath symmetryLineEntPath; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entSymId, ptEntSym, symmetryLineEntPath)) != eOk) return es; aEdgePaths.append(symmetryLineEntPath); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aEdgePaths, pConsGeoms)) != eOk) return es; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; callback._fixedGeom.append(pConsGeoms[0]->nodeId()); callback._fixedGeom.append(pConsGeoms[2]->nodeId()); es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kSymmetric, aEdgePaths, &callback); return es; } OdResult createSymmetricConstraint_TwoVertices(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, // ObjectID of first entity const OdDbObjectId& entId2, // ObjectID of second entity const OdDbObjectId& entSymId, // ObjectID of third entity const OdGePoint3d& ptEnt1, // Point on first entity used to determine which edge subentity is closest to it const OdGePoint3d& ptEnt2, // Point on second entity used to determine which edge subentity is closest to it const OdGePoint3d& ptEntSym) // Point on third entity used to determine which edge subentity is closest to it { SymmetricArgsValidator argsValidator; argsValidator.setModificator(CoincidentArgsValidator::Modificator::SymmetricTwoPoints); bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); bool isValidCrv3 = argsValidator.isEntityValidForConstraint(entSymId, ptEntSym); if (!isValidCrv1 || !isValidCrv2 || !isValidCrv3) { return eInvalidInput; } OdDbFullSubentPathArray aPathsCreateConstrGeoms; OdDbFullSubentPathArray aPathsCreateConstraint; OdDbFullSubentPathArray pathsRequiresMidPoint; bool addMidPoint = false; OdResult es = eOk; OdGe::EntityId type2 = DWGConstraintUtils::entityType(entId2, ptEnt2); OdDbFullSubentPath edgeEntPath1, vertEntPath1; if (DWGConstraintUtils::isText(entId1) || DWGConstraintUtils::isPoint(entId1)) { if ((es = DWGConstraintUtils::getClosestVertexPath(entId1, ptEnt1, vertEntPath1)) != eOk) return es; aPathsCreateConstrGeoms.append(vertEntPath1); } else if (DWGConstraintUtils::isCircle(entId1) || DWGConstraintUtils::isEllipse(entId1)) { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, NULL, NULL, &vertEntPath1)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId1, ptEnt1, vertEntPath1)) != eOk) return es; aPathsCreateConstrGeoms.append(edgeEntPath1); } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, addMidPoint, NULL, NULL, &vertEntPath1)) != eOk) return es; aPathsCreateConstrGeoms.append(edgeEntPath1); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath1); } aPathsCreateConstraint.append(vertEntPath1); OdDbFullSubentPath edgeEntPath2, edgeEntPathBeg2, edgeEntPathEnd2, vertEntPath2; if (DWGConstraintUtils::isText(entId2) || DWGConstraintUtils::isPoint(entId2)) { if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aPathsCreateConstrGeoms.append(vertEntPath2); } else if (DWGConstraintUtils::isCircle(entId2) || DWGConstraintUtils::isEllipse(entId2)) { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, NULL, NULL, &vertEntPath2)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aPathsCreateConstrGeoms.append(edgeEntPath2); } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, addMidPoint, NULL, NULL, &vertEntPath2, &edgeEntPathBeg2, &edgeEntPathEnd2)) != eOk) return es; aPathsCreateConstrGeoms.append(edgeEntPath2); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath2); } aPathsCreateConstraint.append(vertEntPath2); OdDbFullSubentPath symmetryLineEntPath; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entSymId, ptEntSym, symmetryLineEntPath)) != eOk) return es; aPathsCreateConstrGeoms.append(symmetryLineEntPath); aPathsCreateConstraint.append(symmetryLineEntPath); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPathsCreateConstrGeoms, pathsRequiresMidPoint, pConsGeoms)) != eOk) return es; bool differentEntities = (entId1 != entId2); DWGConstraintUtils::DwgConstraintEvaluationCallback callback; // fix first entity picked point DWGConstraintUtils::addHintGeometryFixation(pDatabase, vertEntPath1, callback); if (differentEntities) { // Constraint two point on two different entities DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); if (type2 == OdGe::kLine3d || type2 == OdGe::kLinearEnt3d || type2 == OdGe::kRay3d || type2 == OdGe::kLineSeg3d) { DWGConstraintUtils::addHintAngleFixation(pConsGeoms[1]->nodeId(), callback); DWGConstraintUtils::addHintDistanceFixation(pDatabase, edgeEntPathBeg2, edgeEntPathEnd2, callback); } else if (type2 == OdGe::kCircArc3d || type2 == OdGe::kEllipArc3d) { OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); DWGConstraintUtils::addHintArcFixation(pDatabase, centerPath2, pConsGeoms[1]->nodeId(), callback); } } else { // Constraint two points on the same entity if (type2 == OdGe::kCircArc3d /*|| type2 == OdGe::kEllipArc3d*/) { OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); DWGConstraintUtils::addHintGeometryFixation(pDatabase, centerPath2, callback); } } DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[2]->nodeId(), callback); // Fixate symmetry line es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kSymmetric, aPathsCreateConstraint, &callback); return es; } OdResult DwgConstraintCreation::createSymmetricConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, // ObjectID of first entity const OdDbObjectId& entId2, // ObjectID of second entity const OdDbObjectId& entSymId, // ObjectID of third entity const OdGePoint3d& ptEnt1, // Point on first entity used to determine which edge subentity is closest to it const OdGePoint3d& ptEnt2, // Point on second entity used to determine which edge subentity is closest to it const OdGePoint3d& ptEntSym, // Point on third entity used to determine which edge subentity is closest to it bool bTwoVertices) ODRX_NOEXCEPT // If true, make symmetric two vertices, not entire entities { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; if (!bTwoVertices) return createSymmetricConstraint_TwoEntities(pDatabase, entId1, entId2, entSymId, ptEnt1, ptEnt2, ptEntSym); else return createSymmetricConstraint_TwoVertices(pDatabase, entId1, entId2, entSymId, ptEnt1, ptEnt2, ptEntSym); } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createEqualConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; OdResult es = eOk; OdGe::EntityId type1 = DWGConstraintUtils::entityType(entId1, ptEnt1); OdGe::EntityId type2 = DWGConstraintUtils::entityType(entId2, ptEnt2); EqualArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); if (!isValidCrv1 || !isValidCrv2) { return eInvalidInput; } const bool bArcFirst = type1 == OdGe::kCircArc3d; if (bArcFirst && type2 != OdGe::kCircArc3d) { return eInvalidInput; } if (!bArcFirst && type2 == OdGe::kCircArc3d) { return eInvalidInput; } OdDbFullSubentPathArray aEdgePaths; OdDbFullSubentPath edgeEntPath1, edgeEntPathBeg1, edgeEntPathEnd1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, NULL, NULL, NULL, &edgeEntPathBeg1, &edgeEntPathEnd1)) != eOk) return es; aEdgePaths.append(edgeEntPath1); OdDbFullSubentPath edgeEntPath2, edgeEntPathBeg2; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, NULL, NULL, NULL, &edgeEntPathBeg2)) != eOk) return es; aEdgePaths.append(edgeEntPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aEdgePaths, pConsGeoms)) != eOk) return es; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; callback._fixedGeom.append(pConsGeoms[0]->nodeId()); if (!bArcFirst) { // fix second seg start point OdConstrainedGeometry* pPointGeomBeg = NULL; if (DWGConstraintUtils::getConstrainedGeometry(pDatabase, edgeEntPathBeg2, pPointGeomBeg) == eOk) { callback._fixedGeom.append(pPointGeomBeg->nodeId()); } // fix first seg len OdConstrainedGeometry* pPointGeomEnd = NULL; if (DWGConstraintUtils::getConstrainedGeometry(pDatabase, edgeEntPathBeg1, pPointGeomBeg) == eOk && DWGConstraintUtils::getConstrainedGeometry(pDatabase, edgeEntPathEnd1, pPointGeomEnd) == eOk) { DWGConstraintUtils::DistanceFixation& distanceFixation = *callback._fixedDistance.append(); distanceFixation.pt1 = pPointGeomBeg->nodeId(); distanceFixation.pt2 = pPointGeomEnd->nodeId(); } } es = DWGConstraintUtils::addGeomConstraint(pDatabase, bArcFirst ? OdGeomConstraint::kEqualRadius : OdGeomConstraint::kEqualLength, aEdgePaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } /** \details Apply coincident constraints to two points. */ OdResult createCoincidentConstraint_twopoints(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { OdResult es = eOk; CoincidentArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); if (!isValidCrv1 || !isValidCrv2) { return eInvalidInput; } OdDbFullSubentPathArray aPaths; OdDbFullSubentPathArray aVertexPaths; OdDbFullSubentPathArray pathsRequiresMidPoint; bool addMidPoint = false; OdDbFullSubentPath edgeEntPath1, vertEntPath1; if (DWGConstraintUtils::isText(entId1) || DWGConstraintUtils::isPoint(entId1)) { if ((es = DWGConstraintUtils::getClosestVertexPath(entId1, ptEnt1, vertEntPath1)) != eOk) return es; aPaths.append(vertEntPath1); } else if (DWGConstraintUtils::isCircle(entId1) || DWGConstraintUtils::isEllipse(entId1)) { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, NULL, NULL, &vertEntPath1)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId1, ptEnt1, vertEntPath1)) != eOk) return es; aPaths.append(edgeEntPath1); } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, addMidPoint, NULL, NULL, &vertEntPath1)) != eOk) return es; aPaths.append(edgeEntPath1); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath1); } aVertexPaths.append(vertEntPath1); OdDbFullSubentPath edgeEntPath2, edgeEntPathBeg2, edgeEntPathEnd2, vertEntPath2; if (DWGConstraintUtils::isText(entId2) || DWGConstraintUtils::isPoint(entId2)) { if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aPaths.append(vertEntPath2); } else if (DWGConstraintUtils::isCircle(entId2) || DWGConstraintUtils::isEllipse(entId2)) { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, NULL, NULL, &vertEntPath2)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aPaths.append(edgeEntPath2); } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, addMidPoint, NULL, NULL, &vertEntPath2, &edgeEntPathBeg2, &edgeEntPathEnd2)) != eOk) return es; aPaths.append(edgeEntPath2); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath2); } aVertexPaths.append(vertEntPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPaths, pathsRequiresMidPoint, pConsGeoms)) != eOk) return es; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); // fix first entity picked point DWGConstraintUtils::addHintGeometryFixation(pDatabase, vertEntPath1, callback); // Add second entity auxiliary fixations if (!DWGConstraintUtils::isText(entId2) && !DWGConstraintUtils::isPoint(entId2)) { OdGe::EntityId curve2Type = DWGConstraintUtils::entityType(entId2, ptEnt2); if (curve2Type == OdGe::kLine3d || curve2Type == OdGe::kLinearEnt3d || curve2Type == OdGe::kRay3d || curve2Type == OdGe::kLineSeg3d) { DWGConstraintUtils::addHintAngleFixation(pConsGeoms[1]->nodeId(), callback); // fix second seg len DWGConstraintUtils::addHintDistanceFixation(pDatabase, edgeEntPathBeg2, edgeEntPathEnd2, callback); } else if (curve2Type == OdGe::kCircArc3d || curve2Type == OdGe::kEllipArc3d) { OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); DWGConstraintUtils::addHintArcFixation(pDatabase, centerPath2, pConsGeoms[1]->nodeId(), callback); // fix arc start/end angles #if 0 OdDbFullSubentPath ptBegPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kStart)); OdDbFullSubentPath ptEndPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kEnd)); OdConstrainedGeometry* pPointGeomBeg = NULL; OdConstrainedGeometry* pPointGeomEnd = NULL; if (DWGConstraintUtils::getConstrainedGeometry(pDatabase, ptBegPath2, pPointGeomBeg) == eOk && DWGConstraintUtils::getConstrainedGeometry(pDatabase, ptEndPath2, pPointGeomEnd) == eOk) { DWGConstraintUtils::AngleFixation& angleFixationBeg = *callback._fixedAngle.append(); angleFixationBeg.pt1 = pPointGeomCenter->nodeId(); angleFixationBeg.pt2 = pPointGeomBeg->nodeId(); DWGConstraintUtils::AngleFixation& angleFixationEnd = *callback._fixedAngle.append(); angleFixationEnd.pt1 = pPointGeomCenter->nodeId(); angleFixationEnd.pt2 = pPointGeomEnd->nodeId(); } #endif } } es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kCoincident, aVertexPaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } /** \details Apply coincident constraints to an object. */ OdResult createCoincidentConstraint_curvepoint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) { try { OdResult es = eOk; CoincidentArgsValidator argsValidator; argsValidator.setModificator(ConstraintArgsValidator::Modificator::CoincidentObjectPoint); bool isValid1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValid2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); if (!isValid1 || !isValid2 || DWGConstraintUtils::isText(entId1) || DWGConstraintUtils::isPoint(entId1)) return eInvalidInput; OdDbFullSubentPathArray aCoincidentObjsPaths; // Paths to subentities to create coincident constraint OdDbFullSubentPathArray aConstrainedGeomsPaths; // Paths to subentities to create constrained geometry OdDbFullSubentPathArray pathsRequiresMidPoint; bool addMidPoint = false; OdDbFullSubentPath edgeEntPath1, vertEntPath1, edgeEntPathBeg1, edgeEntPathEnd1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, NULL, NULL, &vertEntPath1, &edgeEntPathBeg1, &edgeEntPathEnd1)) != eOk) return es; aCoincidentObjsPaths.append(edgeEntPath1); aConstrainedGeomsPaths.append(edgeEntPath1); OdDbFullSubentPath edgeEntPath2, edgeEntPathBeg2, edgeEntPathEnd2, vertEntPath2; if (DWGConstraintUtils::isText(entId2) || DWGConstraintUtils::isPoint(entId2)) { if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aCoincidentObjsPaths.append(vertEntPath2); } else if (DWGConstraintUtils::isCircle(entId2) || DWGConstraintUtils::isEllipse(entId2)) { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, NULL, NULL, &vertEntPath2)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aCoincidentObjsPaths.append(vertEntPath2); aConstrainedGeomsPaths.append(edgeEntPath2); } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, addMidPoint, NULL, NULL, &vertEntPath2, &edgeEntPathBeg2, &edgeEntPathEnd2)) != eOk) return es; aCoincidentObjsPaths.append(vertEntPath2); aConstrainedGeomsPaths.append(edgeEntPath2); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath2); } OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aConstrainedGeomsPaths, pathsRequiresMidPoint, pConsGeoms)) != eOk) return es; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; // Fix first entity DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); // Add second entity auxiliary fixations if (!DWGConstraintUtils::isText(entId2) && !DWGConstraintUtils::isPoint(entId2)) { OdGe::EntityId curve2Type = DWGConstraintUtils::entityType(entId2, ptEnt2); if (curve2Type == OdGe::kLine3d || curve2Type == OdGe::kLinearEnt3d || curve2Type == OdGe::kRay3d || curve2Type == OdGe::kLineSeg3d) { DWGConstraintUtils::addHintAngleFixation(pConsGeoms[1]->nodeId(), callback); DWGConstraintUtils::addHintDistanceFixation(pDatabase, edgeEntPathBeg2, edgeEntPathEnd2, callback); } else if (curve2Type == OdGe::kCircArc3d || curve2Type == OdGe::kEllipArc3d) { OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); DWGConstraintUtils::addHintArcFixation(pDatabase, centerPath2, pConsGeoms[1]->nodeId(), callback); } } if ((es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kCoincident, aCoincidentObjsPaths, &callback)) != eOk) return es; return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createCoincidentConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2, bool bPointToCurve /*= false*/) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; return bPointToCurve ? createCoincidentConstraint_curvepoint(pDatabase, entId1, entId2, ptEnt1, ptEnt2) : createCoincidentConstraint_twopoints(pDatabase, entId1, entId2, ptEnt1, ptEnt2); } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createCoincidentConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdDbFullSubentPath& vertexSubentPath1, const OdDbFullSubentPath& vertexSubentPath2) ODRX_NOEXCEPT { try { CoincidentArgsValidator argsValidator; if (!argsValidator.isVerticesValidForConstraint(entId1, vertexSubentPath1, vertexSubentPath2)) { return eInvalidInput; } OdResult es = eOk; OdDbFullSubentPathArray aEdgePathsTmp1; es = DWGConstraintUtils::getEdgeSubEntPathByVertexPath(entId1.safeOpenObject(), vertexSubentPath1, aEdgePathsTmp1); if (es != eOk) return es; OdDbFullSubentPathArray aEdgePathsTmp2; es = DWGConstraintUtils::getEdgeSubEntPathByVertexPath(entId2.safeOpenObject(), vertexSubentPath2, aEdgePathsTmp2); if (es != eOk) return es; // There is possible from 2 to 4 edge paths but to create constraint only 2 of them needed. OdDbFullSubentPath edgePath1, edgePath2; if (aEdgePathsTmp1.logicalLength() == 1 && aEdgePathsTmp2.logicalLength() == 1) { edgePath1 = aEdgePathsTmp1[0]; edgePath2 = aEdgePathsTmp2[0]; if (edgePath1 == edgePath2) return eInvalidInput; } else if (aEdgePathsTmp1.logicalLength() == 1 && aEdgePathsTmp2.logicalLength() == 2) { edgePath1 = aEdgePathsTmp1[0]; if(edgePath1 == aEdgePathsTmp2[0]) edgePath2 = aEdgePathsTmp2[0]; else edgePath2 = aEdgePathsTmp2[1]; } else if (aEdgePathsTmp1.logicalLength() == 2 && aEdgePathsTmp2.logicalLength() == 1) { edgePath2 = aEdgePathsTmp2[0]; if (edgePath2 == aEdgePathsTmp1[0]) edgePath1 = aEdgePathsTmp1[0]; else edgePath1 = aEdgePathsTmp1[1]; } else if (aEdgePathsTmp1.logicalLength() == 2 && aEdgePathsTmp2.logicalLength() == 2) { edgePath1 = aEdgePathsTmp1[0]; if (edgePath1 == aEdgePathsTmp2[0]) edgePath2 = aEdgePathsTmp2[0]; else edgePath2 = aEdgePathsTmp2[1]; } else { return eInvalidInput; } OdDbFullSubentPathArray aPaths; OdDbFullSubentPathArray aVertexPaths; OdDbFullSubentPathArray pathsRequiresMidPoint; aPaths.append(edgePath1); aVertexPaths.append(vertexSubentPath1); aPaths.append(edgePath2); aVertexPaths.append(vertexSubentPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPaths, pathsRequiresMidPoint, pConsGeoms)) != eOk) return es; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); // fix first entity picked point OdDbFullSubentPath fixedVertSubentPath = vertexSubentPath1; DWGConstraintUtils::addHintGeometryFixation(pDatabase, fixedVertSubentPath, callback); es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kCoincident, aVertexPaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createCollinearConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; OdResult es = eOk; LinesConstraintArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); if (!(isValidCrv1 && isValidCrv2)) return eInvalidInput; OdDbFullSubentPathArray aPaths; OdDbFullSubentPath subentPath1; if (DWGConstraintUtils::isText(entId1) || DWGConstraintUtils::isEllipse(entId1)) { if ((es = DWGConstraintUtils::getClosestAxis(entId1, ptEnt1, subentPath1)) != eOk) return es; } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, subentPath1)) != eOk) return es; } aPaths.append(subentPath1); OdDbFullSubentPath subentPath2, pointEntPath2, edgeEntPathBeg2, edgeEntPathEnd2; if (DWGConstraintUtils::isText(entId2) || DWGConstraintUtils::isEllipse(entId2)) { if ((es = DWGConstraintUtils::getClosestAxis(entId2, ptEnt2, subentPath2)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, pointEntPath2)) != eOk) return es; } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, subentPath2, NULL, NULL, &pointEntPath2, &edgeEntPathBeg2, &edgeEntPathEnd2)) != eOk) return es; } aPaths.append(subentPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPaths, pConsGeoms)) != eOk) return es; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); // fix second seg len DWGConstraintUtils::addHintDistanceFixation(pDatabase, edgeEntPathBeg2, edgeEntPathEnd2, callback); es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kColinear, aPaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createConcentricConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; OdResult es = eOk; ConcentricArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); if (!(isValidCrv1 && isValidCrv2)) return eInvalidInput; OdDbFullSubentPathArray aEdgePaths; OdDbFullSubentPath edgeEntPath1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1)) != eOk) return es; aEdgePaths.append(edgeEntPath1); OdDbFullSubentPath edgeEntPath2; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2)) != eOk) return es; aEdgePaths.append(edgeEntPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aEdgePaths, pConsGeoms)) != eOk) return es; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); DWGConstraintUtils::addHintArcFixation(pDatabase, centerPath2, pConsGeoms[1]->nodeId(), callback); // fix arc start/end angles #if 0 OdDbFullSubentPath ptBegPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kStart)); OdDbFullSubentPath ptEndPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kEnd)); OdConstrainedGeometry* pPointGeomBeg = NULL; OdConstrainedGeometry* pPointGeomEnd = NULL; if (DWGConstraintUtils::getConstrainedGeometry(pDatabase, ptBegPath2, pPointGeomBeg) == eOk && DWGConstraintUtils::getConstrainedGeometry(pDatabase, ptEndPath2, pPointGeomEnd) == eOk) { DWGConstraintUtils::AngleFixation& angleFixationBeg = *callback._fixedAngle.append(); angleFixationBeg.fixedAxisHint = pConsGeoms[0]->nodeId(); angleFixationBeg.pt1 = pPointGeomCenter->nodeId(); angleFixationBeg.pt2 = pPointGeomBeg->nodeId(); DWGConstraintUtils::AngleFixation& angleFixationEnd = *callback._fixedAngle.append(); angleFixationEnd.fixedAxisHint = pConsGeoms[0]->nodeId(); angleFixationEnd.pt1 = pPointGeomCenter->nodeId(); angleFixationEnd.pt2 = pPointGeomEnd->nodeId(); } #endif es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kConcentric, aEdgePaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createFixConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId, const OdGePoint3d& ptEnt, bool bFixEntireObj /*= false*/) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; OdResult es = eOk; FixArgsValidator argsValidator; if (bFixEntireObj) argsValidator.setModificator(ConstraintArgsValidator::Modificator::FixEntireObj); bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId, ptEnt); if (!isValidCrv1) return eInvalidInput; OdDbFullSubentPathArray aEdgePaths; OdDbFullSubentPathArray aVertexPaths; OdDbFullSubentPathArray pathsRequiresMidPoint; bool addMidPoint = false; if (!(DWGConstraintUtils::isText(entId) || DWGConstraintUtils::isPoint(entId))) { OdDbFullSubentPath edgeEntPath1, vertEntPath1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId, ptEnt, edgeEntPath1, addMidPoint, NULL, NULL, &vertEntPath1, NULL, NULL)) != eOk) return es; aEdgePaths.append(edgeEntPath1); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath1); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aEdgePaths, pathsRequiresMidPoint, pConsGeoms)) != eOk) return es; if (!bFixEntireObj) { if (vertEntPath1.objectIds().length() == 0) // Object has no vertices. It's circle or ellipse if ((es = DWGConstraintUtils::getCenterVertexPath(entId, vertEntPath1)) != eOk) return es; aVertexPaths.append(vertEntPath1); es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kFix, aVertexPaths, NULL); } else { es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kFix, aEdgePaths, NULL); } } else { OdDbEntityPtr pEnt = entId.openObject(OdDb::kForRead); if ((es = DWGConstraintUtils::getFullSubentPaths(pEnt, OdDb::kVertexSubentType, aVertexPaths)) != eOk) return es; OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aVertexPaths, pConsGeoms)) != eOk) return es; es = DWGConstraintUtils::addGeomConstraint(pDatabase, OdGeomConstraint::kFix, aVertexPaths, NULL); } return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createHorizontalOrVerticalConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId, const OdGePoint3d& ptEnt, bool bVertical) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; const OdGeomConstraint::GeomConstraintType constraintType = bVertical ? OdGeomConstraint::kVertical : OdGeomConstraint::kHorizontal; OdResult es = eOk; HorizontalVerticalArgsValidator argsValidator; bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId, ptEnt); if (!isValidCrv1) return eInvalidInput; OdDbFullSubentPathArray aPaths; DWGConstraintUtils::DwgConstraintEvaluationCallback callback; OdArray pConsGeoms; if (DWGConstraintUtils::isText(entId) || DWGConstraintUtils::isEllipse(entId)) { OdDbFullSubentPath axisPath; if ((es = DWGConstraintUtils::getClosestAxis(entId, ptEnt, axisPath)) != eOk) return es; aPaths.append(axisPath); if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPaths, pConsGeoms)) != eOk) return es; OdDbFullSubentPath pointPath; if ((es = DWGConstraintUtils::getClosestVertexPath(entId, ptEnt, pointPath)) != eOk) return es; DWGConstraintUtils::addHintGeometryFixation(pDatabase, pointPath, callback); } else { OdDbFullSubentPath edgeEntPath1, pointEntPath1, edgeEntPathBeg1, edgeEntPathEnd1; if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId, ptEnt, edgeEntPath1, NULL, NULL, &pointEntPath1, &edgeEntPathBeg1, &edgeEntPathEnd1)) != eOk) return es; aPaths.append(edgeEntPath1); if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPaths, pConsGeoms)) != eOk) return es; // fix picked end DWGConstraintUtils::addHintGeometryFixation(pDatabase, pointEntPath1, callback); // fix seg len DWGConstraintUtils::addHintDistanceFixation(pDatabase, edgeEntPathBeg1, edgeEntPathEnd1, callback); } es = DWGConstraintUtils::addGeomConstraint(pDatabase, constraintType, aPaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } OdResult DwgConstraintCreation::createHorizontalOrVerticalConstraint(OdDbDatabase* pDatabase, const OdDbObjectId& entId1, const OdDbObjectId& entId2, const OdGePoint3d& ptEnt1, const OdGePoint3d& ptEnt2, bool bVertical) ODRX_NOEXCEPT { try { if (!DWGConstraintUtils::moduleIsLoaded()) return eNotApplicable; const OdGeomConstraint::GeomConstraintType constraintType = bVertical ? OdGeomConstraint::kVertical : OdGeomConstraint::kHorizontal; OdResult es = eOk; HorizontalVerticalArgsValidator argsValidator; argsValidator.setModificator(ConstraintArgsValidator::Modificator::HorVertTwoPoins); bool isValidCrv1 = argsValidator.isEntityValidForConstraint(entId1, ptEnt1); bool isValidCrv2 = argsValidator.isEntityValidForConstraint(entId2, ptEnt2); OdGe::EntityId type1 = DWGConstraintUtils::entityType(entId1, ptEnt1); (void)type1; OdGe::EntityId type2 = DWGConstraintUtils::entityType(entId2, ptEnt2); if (!isValidCrv1 || !isValidCrv2) return eInvalidInput; OdDbFullSubentPathArray aPaths; OdDbFullSubentPathArray aVertexPaths; OdDbFullSubentPathArray pathsRequiresMidPoint; bool addMidPoint = false; OdDbFullSubentPath edgeEntPath1, vertEntPath1; if (DWGConstraintUtils::isText(entId1) || DWGConstraintUtils::isPoint(entId1)) { if ((es = DWGConstraintUtils::getClosestVertexPath(entId1, ptEnt1, vertEntPath1)) != eOk) return es; aPaths.append(vertEntPath1); } else if (DWGConstraintUtils::isCircle(entId1) || DWGConstraintUtils::isEllipse(entId1)) { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, NULL, NULL, &vertEntPath1)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId1, ptEnt1, vertEntPath1)) != eOk) return es; aPaths.append(edgeEntPath1); } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId1, ptEnt1, edgeEntPath1, addMidPoint, NULL, NULL, &vertEntPath1)) != eOk) return es; aPaths.append(edgeEntPath1); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath1); } aVertexPaths.append(vertEntPath1); OdDbFullSubentPath edgeEntPath2, edgeEntPathBeg2, edgeEntPathEnd2, vertEntPath2; if (DWGConstraintUtils::isText(entId2) || DWGConstraintUtils::isPoint(entId2)) { if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aPaths.append(vertEntPath2); } else if (DWGConstraintUtils::isCircle(entId2) || DWGConstraintUtils::isEllipse(entId2)) { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, NULL, NULL, &vertEntPath2)) != eOk) return es; if ((es = DWGConstraintUtils::getClosestVertexPath(entId2, ptEnt2, vertEntPath2)) != eOk) return es; aPaths.append(edgeEntPath2); } else { if ((es = DWGConstraintUtils::getClosestEdgeSubEntPath(entId2, ptEnt2, edgeEntPath2, addMidPoint, NULL, NULL, &vertEntPath2, &edgeEntPathBeg2, &edgeEntPathEnd2)) != eOk) return es; aPaths.append(edgeEntPath2); if (addMidPoint) pathsRequiresMidPoint.append(edgeEntPath2); } aVertexPaths.append(vertEntPath2); OdArray pConsGeoms; if ((es = DWGConstraintUtils::addConstrainedGeometry(pDatabase, aPaths, pathsRequiresMidPoint, pConsGeoms)) != eOk) return es; bool differentEntities = (entId1 != entId2); DWGConstraintUtils::DwgConstraintEvaluationCallback callback; // fix first entity picked point DWGConstraintUtils::addHintGeometryFixation(pDatabase, vertEntPath1, callback); if (differentEntities) { // Constraint two point on two different entities DWGConstraintUtils::addHintGeometryFixation(pConsGeoms[0]->nodeId(), callback); if (type2 == OdGe::kLine3d || type2 == OdGe::kLinearEnt3d || type2 == OdGe::kRay3d || type2 == OdGe::kLineSeg3d) { DWGConstraintUtils::addHintAngleFixation(pConsGeoms[1]->nodeId(), callback); DWGConstraintUtils::addHintDistanceFixation(pDatabase, edgeEntPathBeg2, edgeEntPathEnd2, callback); } else if (type2 == OdGe::kCircArc3d || type2 == OdGe::kEllipArc3d) { OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); DWGConstraintUtils::addHintArcFixation(pDatabase, centerPath2, pConsGeoms[1]->nodeId(), callback); } } else { // Constraint two points on the same entity if (type2 == OdGe::kCircArc3d /*|| type2 == OdGe::kEllipArc3d*/) { OdDbFullSubentPath centerPath2(entId2, OdDbSubentId(OdDb::kVertexSubentType, OdDbAssocCurvePersSubentIdPE::kCenter)); } } es = DWGConstraintUtils::addGeomConstraint(pDatabase, constraintType, aVertexPaths, &callback); return es; } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } } struct EntitySolverObjects { OdVector aEntSolver; OdGePoint3d ptEnt; double ptEntParam = 0.; // for help param OdDbObjectId id; int iPolySeg = -1; }; static void createSolverEntity(const OdDbObjectIdArray& aEntId, const OdGeMatrix3d& matUCS, OdConstraints& constraints, OdVector& result) { OdGePlane planeUCS; { OdGePoint3d origin; OdGeVector3d xAxis; OdGeVector3d yAxis; OdGeVector3d zAxis; matUCS.getCoordSystem(origin, xAxis, yAxis, zAxis); planeUCS.set(origin, xAxis, yAxis); } const int nEnt = aEntId.size(); for (int iEnt = 0; iEnt < nEnt; ++iEnt) { const OdDbObjectId& id = aEntId[iEnt]; OdDbEntityPtr pEnt = id.openObject(OdDb::kForRead); if (pEnt.isNull() || pEnt->isErased()) { continue; } OdGePlane planeEnt; OdDb::Planarity planarity; pEnt->getPlane(planeEnt, planarity); if (planarity == OdDb::kNonPlanar) { continue; } if (planarity == OdDb::kPlanar && !planeEnt.normal().isParallelTo(matUCS.getCsZAxis())) { continue; } EntitySolverObjects csEntity; csEntity.id = id; if (pEnt->isKindOf(OdDbLine::desc())) { OdDbLinePtr linePtr = OdDbLine::cast(pEnt); OdGePoint3d start(linePtr->startPoint()); OdGePoint3d end(linePtr->endPoint()); if (!planeUCS.isOnPlane(start) || !planeUCS.isOnPlane(end)) { continue; } start.transformBy(matUCS); end.transformBy(matUCS); OdCsGeometryPtr entSolver = OdCsLine::create(start.convert2d(), (end - start).convert2d()); csEntity.aEntSolver.emplace_back(entSolver); OdCsGeometryPtr csStart = OdCsPoint::create(start.convert2d()); csEntity.aEntSolver.emplace_back(csStart); constraints.emplace_back(OdCsCoincidence::create(entSolver, csStart)); OdCsGeometryPtr csEnd = OdCsPoint::create(end.convert2d()); csEntity.aEntSolver.emplace_back(csEnd); constraints.emplace_back(OdCsCoincidence::create(entSolver, csEnd)); csEntity.ptEnt = start; } else if (pEnt->isKindOf(OdDbXline::desc())) { OdDbXlinePtr linePtr = OdDbXline::cast(pEnt); OdGePoint3d start(linePtr->basePoint()); OdGePoint3d end(start + linePtr->unitDir()); if (!planeUCS.isOnPlane(start) || !planeUCS.isOnPlane(end)) { continue; } start.transformBy(matUCS); end.transformBy(matUCS); OdCsGeometryPtr entSolver = OdCsLine::create(start.convert2d(), (end - start).convert2d()); csEntity.aEntSolver.emplace_back(entSolver); csEntity.ptEnt = start; } else if (pEnt->isKindOf(OdDbRay::desc())) { OdDbRayPtr linePtr = OdDbRay::cast(pEnt); OdGePoint3d start(linePtr->basePoint()); OdGePoint3d end(start + linePtr->unitDir()); if (!planeUCS.isOnPlane(start) || !planeUCS.isOnPlane(end)) { continue; } start.transformBy(matUCS); end.transformBy(matUCS); OdCsGeometryPtr entSolver = OdCsLine::create(start.convert2d(), (end - start).convert2d()); csEntity.aEntSolver.emplace_back(entSolver); OdCsGeometryPtr csStart = OdCsPoint::create(start.convert2d()); csEntity.aEntSolver.emplace_back(csStart); constraints.emplace_back(OdCsCoincidence::create(entSolver, csStart)); csEntity.ptEnt = start; } else if (pEnt->isKindOf(OdDbCircle::desc())) { OdDbCirclePtr arcPtr = OdDbCircle::cast(pEnt); OdGePoint3d center = arcPtr->center(); if (!planeUCS.isOnPlane(center)) { continue; } center.transformBy(matUCS); OdCsGeometryPtr entSolver = OdCsCircle::create(center.convert2d(), arcPtr->radius()); csEntity.aEntSolver.emplace_back(entSolver); arcPtr->getPointAtParam(0., csEntity.ptEnt); } else if (pEnt->isKindOf(OdDbArc::desc())) { OdDbArcPtr arcPtr = OdDbArc::cast(pEnt); OdGePoint3d center = arcPtr->center(); OdGePoint3d start; OdGePoint3d end; arcPtr->getStartPoint(start); arcPtr->getEndPoint(end); if (!planeUCS.isOnPlane(center) || !planeUCS.isOnPlane(start) || !planeUCS.isOnPlane(end)) { continue; } center.transformBy(matUCS); start.transformBy(matUCS); end.transformBy(matUCS); OdCsGeometryPtr entSolver = OdCsCircle::create(center.convert2d(), arcPtr->radius()); csEntity.aEntSolver.emplace_back(entSolver); OdCsGeometryPtr csStart = OdCsPoint::create(start.convert2d()); csEntity.aEntSolver.emplace_back(csStart); constraints.emplace_back(OdCsCoincidence::create(entSolver, csStart)); OdCsGeometryPtr csEnd = OdCsPoint::create(end.convert2d()); csEntity.aEntSolver.emplace_back(csEnd); constraints.emplace_back(OdCsCoincidence::create(entSolver, csEnd)); csEntity.ptEnt = start; } else if (pEnt->isKindOf(OdDbEllipse::desc())) { OdDbEllipsePtr arcPtr = OdDbEllipse::cast(pEnt); OdGePoint3d center = arcPtr->center(); OdGePoint3d start, end; arcPtr->getStartPoint(start); arcPtr->getEndPoint(end); if (!planeUCS.isOnPlane(center) || !planeUCS.isOnPlane(start) || !planeUCS.isOnPlane(end)) { continue; } center.transformBy(matUCS); start.transformBy(matUCS); end.transformBy(matUCS); OdGeVector3d majorAxis = arcPtr->majorAxis(); majorAxis.transformBy(matUCS); const double rMajor = majorAxis.length(); OdCsGeometryPtr entSolver = OdCsEllipse::create(center.convert2d(), majorAxis.convert2d(), rMajor, rMajor * arcPtr->radiusRatio()); csEntity.aEntSolver.emplace_back(entSolver); if (!start.isEqualTo(end)) { OdCsGeometryPtr csStart = OdCsPoint::create(start.convert2d()); csEntity.aEntSolver.emplace_back(csStart); constraints.emplace_back(OdCsCoincidence::create(entSolver, csStart)); OdCsGeometryPtr csEnd = OdCsPoint::create(end.convert2d()); csEntity.aEntSolver.emplace_back(csEnd); constraints.emplace_back(OdCsCoincidence::create(entSolver, csEnd)); } // axises { // major OdGePoint2d pt0 = (center + majorAxis).convert2d(); OdGePoint2d ptPI = (center - majorAxis).convert2d(); OdCsGeometryPtr axisU = OdCsLine::create(center.convert2d(), majorAxis.convert2d()); csEntity.aEntSolver.emplace_back(axisU); OdCsGeometryPtr csStart = OdCsPoint::create(pt0); csEntity.aEntSolver.emplace_back(csStart); constraints.emplace_back(OdCsCoincidence::create(axisU, csStart)); constraints.emplace_back(OdCsCoincidence::create(entSolver, csStart)); OdCsGeometryPtr csEnd = OdCsPoint::create(ptPI); csEntity.aEntSolver.emplace_back(csEnd); constraints.emplace_back(OdCsCoincidence::create(axisU, csEnd)); constraints.emplace_back(OdCsCoincidence::create(entSolver, csEnd)); } csEntity.ptEnt = start; } else if (pEnt->isKindOf(OdDbPolyline::desc())) { OdDbPolylinePtr pLinePtr = OdDbPolyline::cast(pEnt); for (unsigned int i = 0; i < pLinePtr->numVerts() - 1; ++i) { double bulge = pLinePtr->getBulgeAt(i); OdGePoint3d start, end; pLinePtr->getPointAt(i, start); pLinePtr->getPointAt(i + 1, end); csEntity.iPolySeg = i; if (OdZero(bulge)) { OdCsGeometryPtr entSolver = OdCsLine::create(start.convert2d(), (end - start).convert2d()); csEntity.aEntSolver.emplace_back(entSolver); OdCsGeometryPtr csStart = OdCsPoint::create(start.convert2d()); csEntity.aEntSolver.emplace_back(csStart); constraints.emplace_back(OdCsCoincidence::create(entSolver, csStart)); OdCsGeometryPtr csEnd = OdCsPoint::create(end.convert2d()); csEntity.aEntSolver.emplace_back(csEnd); constraints.emplace_back(OdCsCoincidence::create(entSolver, csEnd)); csEntity.ptEnt = (start + end.asVector()) * 0.5; } else { double ang = atan(fabs(bulge)) * 2.0; OdGeVector3d vec = end - start; double halfLen = vec.length() * 0.5; double rad = halfLen / sin(ang); double lenToCenter = rad * cos(ang) * (bulge > 0.0 ? 1.0 : -1.0); OdGeVector3d ortho = OdGeVector3d::kZAxis.crossProduct(vec); OdGe::ErrorCondition ec = OdGe::kOk; ortho.normalize(OdGeContext::gZeroTol, ec); ortho *= lenToCenter; OdGePoint3d center((start.x + end.x) / 2.0 + ortho.x, (start.y + end.y) / 2.0 + ortho.y, (start.z + end.z) / 2.0 + ortho.z); OdCsGeometryPtr entSolver = OdCsCircle::create(center.convert2d(), rad); csEntity.aEntSolver.emplace_back(entSolver); OdCsGeometryPtr csStart = OdCsPoint::create(start.convert2d()); csEntity.aEntSolver.emplace_back(csStart); constraints.emplace_back(OdCsCoincidence::create(entSolver, csStart)); OdCsGeometryPtr csEnd = OdCsPoint::create(end.convert2d()); csEntity.aEntSolver.emplace_back(csEnd); constraints.emplace_back(OdCsCoincidence::create(entSolver, csEnd)); pLinePtr->getPointAtParam(i + 0.5, csEntity.ptEnt); } // connect to prev segment if (i > 0) { const auto& prevSegEnt = result.last().aEntSolver; ODA_ASSERT_ONCE(prevSegEnt.size() == 3); ODA_ASSERT_ONCE(csEntity.aEntSolver.size() == 3); constraints.emplace_back(OdCsCoincidence::create(prevSegEnt[2], csEntity.aEntSolver[1])); } // each poly segment is a separate solver entity if (i != pLinePtr->numVerts() - 2) { result.push_back(csEntity); csEntity.aEntSolver.clear(); } } // connect ends if (pLinePtr->isClosed() && pLinePtr->numVerts() > 2) { const unsigned int iFirstSeg = result.size() - (pLinePtr->numVerts() - 2); constraints.emplace_back(OdCsCoincidence::create(result[iFirstSeg].aEntSolver[1], csEntity.aEntSolver[2])); } } else if (pEnt->isKindOf(OdDbSpline::desc())) { OdDbSplinePtr pSplinePtr = OdDbSpline::cast(pEnt); OdGePoint3d start, end; pSplinePtr->getStartPoint(start); pSplinePtr->getEndPoint(end); if (!planeUCS.isOnPlane(start) || !planeUCS.isOnPlane(end)) { continue; } start.transformBy(matUCS); end.transformBy(matUCS); int degree; bool rational, closed, periodic; OdGePoint3dArray controlPoints; OdGeKnotVector knots; OdGeDoubleArray weights; double controlPtTol; pSplinePtr->getNurbsData(degree, rational, closed, periodic, controlPoints, knots, weights, controlPtTol); const unsigned int nCP = controlPoints.logicalLength(); OdGePoint2dArray controlPoints2d; controlPoints2d.resize(nCP); for (unsigned int i = 0; i < nCP; ++i) { controlPoints[i].transformBy(matUCS); controlPoints2d[i] = controlPoints[i].convert2d(); } OdGeNurbCurve2d nurb2d; nurb2d.set(degree, knots, controlPoints2d, weights, periodic); OdCsSplinePtr entSolver = OdCsSpline::create(nurb2d); const OdVector& apControlPoints = entSolver->controlPoints(); csEntity.aEntSolver.emplace_back(entSolver); OdCsGeometryPtr csStart = apControlPoints[0]; csEntity.aEntSolver.emplace_back(csStart); OdCsGeometryPtr csEnd = apControlPoints.last(); csEntity.aEntSolver.emplace_back(csEnd); csEntity.ptEnt = start; } else { ODA_ASSERT_ONCE(0); continue; } result.emplace_back(csEntity); // for debug only //OdDbFullSubentPathArray aVertexPaths; //if (DWGConstraintUtils::getFullSubentPaths(pEnt, OdDb::kVertexSubentType, aVertexPaths) == eOk) //{ // for (OdDbFullSubentPath path : aVertexPaths) // { // OdGsMarker index = path.subentId().index(); // if (index == -1 || index == -3) // { // //OdCsGeometryPtr entSolver = OdCsPoint::create(...); // //csEntity.aEntSolver.emplace_back(entSolver); // } // } //} } } struct SolverSiEntity : public OdSiEntity { SolverSiEntity(const EntitySolverObjects& objects) : m_objects(objects) { const OdCsGeometryPtr& mainEnt = objects.aEntSolver[0]; if (mainEnt->type() == OdCs::kPoint) { const OdCsPoint* csPoint = static_cast(mainEnt.get()); ODA_ASSERT_ONCE(m_objects.aEntSolver.size() == 1); m_extents.set(OdGePoint3d(csPoint->position().x, csPoint->position().y, 0.), OdGePoint3d(csPoint->position().x, csPoint->position().y, 0.)); return; } if (mainEnt->type() == OdCs::kLine) { /*const OdCsLine* csLine = static_cast(mainEnt.get());*/ if (m_objects.aEntSolver.size() < 3) // infinite line { // TODO: ray m_extents.set(OdGePoint3d(-DBL_MAX, -DBL_MAX, 0.), OdGePoint3d(DBL_MAX, DBL_MAX, 0.)); } else { // segment ODA_ASSERT_ONCE(m_objects.aEntSolver.size() == 3); ODA_ASSERT(m_objects.aEntSolver[1]->type() == OdCs::kPoint && m_objects.aEntSolver[2]->type() == OdCs::kPoint); const OdCsPoint* csBeg = static_cast(m_objects.aEntSolver[1].get()); const OdCsPoint* csEnd = static_cast(m_objects.aEntSolver[2].get()); m_extents.addPoint(OdGePoint3d(csBeg->position().x, csBeg->position().y, 0.)); m_extents.addPoint(OdGePoint3d(csEnd->position().x, csEnd->position().y, 0.)); //m_extents.set(m_extents.minPoint() - OdGeVector3d(tol, tol, 0.), m_extents.maxPoint() + OdGeVector3d(tol, tol, 0.)); } return; } // TODO: support arcs, their extents are smaller if (mainEnt->type() == OdCs::kCircle) { const OdCsCircle* csCircle = static_cast(mainEnt.get()); m_extents.set(OdGePoint3d(csCircle->center().x - csCircle->radius(), csCircle->center().y - csCircle->radius(), 0.), OdGePoint3d(csCircle->center().x + csCircle->radius(), csCircle->center().y + csCircle->radius(), 0.)); return; } if (mainEnt->type() == OdCs::kEllipse) { const OdCsEllipse* csEllipse = static_cast(mainEnt.get()); // TODO: not accurate m_extents.set(OdGePoint3d(csEllipse->center().x - csEllipse->majorRadius(), csEllipse->center().y - csEllipse->majorRadius(), 0.), OdGePoint3d(csEllipse->center().x + csEllipse->majorRadius(), csEllipse->center().y + csEllipse->majorRadius(), 0.)); return; } if (mainEnt->type() == OdCs::kSpline) { const OdCsSpline* csSpline = static_cast(mainEnt.get()); const auto& aCP = csSpline->controlPoints(); for (const auto& cp : aCP) { m_extents.addPoint(OdGePoint3d(cp->position().x, cp->position().y, 0.)); } return; } ODA_ASSERT_ONCE(0); } virtual bool extents(OdGeExtents3d& extents) const { extents = m_extents; return true; } const EntitySolverObjects& m_objects; OdGeExtents3d m_extents; }; struct SolverSiShape : public OdSiShape { SolverSiShape(const OdGePoint2d& vx, double tol) : m_vx(vx) , m_tol(tol) {} virtual bool contains(const OdGeExtents3d&, bool, const OdGeTol&) const { return false; } virtual bool intersects(const OdGeExtents3d& extents, bool, const OdGeTol&) const { const OdGePoint3d& b1 = extents.minPoint(); const OdGePoint3d& b2 = extents.maxPoint(); if (m_vx.x < b1.x - m_tol || m_vx.x > b2.x + m_tol || m_vx.y < b1.y - m_tol || m_vx.y > b2.y + m_tol) { return false; } return true; } OdGePoint2d m_vx; double m_tol; }; struct PtEntityCoincidence : public OdSiVisitor { PtEntityCoincidence(const OdGePoint2d& pt, const OdGeMatrix3d& matWCS, OdDbObjectId idIgnore, int iPolySeg, double tol = 1e-10) : m_pt(pt), m_tol(tol), m_matWCS(matWCS), m_idIgnore(idIgnore), m_iPolySeg(iPolySeg) { } virtual void visit(OdSiEntity* pEntity, bool) { SolverSiEntity* pArrayElem = static_cast(pEntity); // entity meets itself (not poly) or poly's segment meets itself if (pArrayElem->m_objects.id == m_idIgnore && (m_iPolySeg < 0 || pArrayElem->m_objects.iPolySeg == m_iPolySeg)) { return; } // check that it's a neighbor segment of the same poly if (pArrayElem->m_objects.id == m_idIgnore && m_iPolySeg >= 0) { if (pArrayElem->m_objects.iPolySeg == m_iPolySeg + 1 || pArrayElem->m_objects.iPolySeg == m_iPolySeg - 1) { return; } } // first check points coincidence bool pointFound = false; for (unsigned int i = 0; i < pArrayElem->m_objects.aEntSolver.size(); ++i) { auto& csEnt = pArrayElem->m_objects.aEntSolver[i]; OdCsPointPtr point = csEnt.dynamicCast(); if (point.isNull()) { continue; } if (point->position().isEqualTo(m_pt, OdGeTol(m_tol))) { EntitySolverObjects res; res.aEntSolver.push_back(point); res.id = pArrayElem->m_objects.id; res.ptEnt = OdGePoint3d(point->position().x, point->position().y, 0.); res.ptEnt.transformBy(m_matWCS); if (pArrayElem->m_objects.iPolySeg >= 0) { res.iPolySeg = i == 1 ? pArrayElem->m_objects.iPolySeg : pArrayElem->m_objects.iPolySeg + 1; } m_resultPoints.emplace_back(res); pointFound = true; } } if (pointFound) { return; } // then curve coincidence if (pArrayElem->m_objects.aEntSolver[0]->type() == OdCs::kSpline) // optimization not to create new geCurve { OdCsSplinePtr csSpline = pArrayElem->m_objects.aEntSolver[0].dynamicCast(); const OdGeNurbCurve2d& geCurve = csSpline->nurbCurve(); double closestParam = 0.; const OdGePoint2d ptOnCurve = geCurve.closestPointTo(m_pt, closestParam, nullptr); if (ptOnCurve.isEqualTo(m_pt, OdGeTol(m_tol))) { EntitySolverObjects res; res.aEntSolver.push_back(pArrayElem->m_objects.aEntSolver[0]); res.id = pArrayElem->m_objects.id; OdGePoint3d ptClosest = OdGePoint3d(ptOnCurve.x, ptOnCurve.y, 0.); ptClosest.transformBy(m_matWCS); res.ptEnt = ptClosest; res.ptEntParam = closestParam; res.iPolySeg = -1; m_resultCurves.emplace_back(res); } return; } OdDbCurvePtr pCurve = pArrayElem->m_objects.id.openObject(OdDb::kForRead); if (!pCurve.isNull()) { OdAutoPtr geCurve; pCurve->getOdGeCurve(geCurve.receive()); OdGePoint3d ptSearch = OdGePoint3d(m_pt.x, m_pt.y, 0.); ptSearch.transformBy(m_matWCS); double closestParam = 0.; const OdGePoint3d ptOnCurve = geCurve->closestPointTo(ptSearch, closestParam, nullptr); if (ptOnCurve.isEqualTo(ptSearch, OdGeTol(m_tol))) { EntitySolverObjects res; res.aEntSolver.push_back(pArrayElem->m_objects.aEntSolver[0]); res.id = pArrayElem->m_objects.id; res.ptEnt = ptOnCurve; res.ptEntParam = closestParam; res.iPolySeg = pArrayElem->m_objects.iPolySeg; m_resultCurves.emplace_back(res); return; } } } const OdGePoint2d& m_pt; double m_tol; const OdGeMatrix3d& m_matWCS; OdDbObjectId m_idIgnore; int m_iPolySeg = -1; OdArray m_resultPoints; OdArray m_resultCurves; }; static bool tryAddConstraint(OdCsConstraintSystem& system, OdCsConstraintPtr solverCnstr, OdCsOverDefinedChecker& checker, OdGeTol tol) { system.constraints().push_back(solverCnstr); OdCsValidator inputValidator(system); inputValidator.setTolerance(tol); bool isValid = false; try { isValid = inputValidator.validate(); } catch (...) { } if (!isValid) { system.constraints().setLogicalLength(system.constraints().logicalLength() - 1); return false; } // overdiagnostics system.constraints().setLogicalLength(system.constraints().logicalLength() - 1); checker.addObject(solverCnstr); OdConstraints overContraints = checker.checkAndGetOverConstraints(); if (!overContraints.isEmpty()) { ODA_ASSERT_ONCE(overContraints.size() == 1 && overContraints[0] == solverCnstr); checker.removeConstraint(solverCnstr); return false; } system.constraints().push_back(solverCnstr); // if need to save a replay if (0) { OdConstraintSolver solver(system); solver.solve(); } return true; } OdResult DwgConstraintCreation::autoConstrain(OdDbDatabase* pDatabase, const OdDbObjectIdArray& aEntId, const OdArray& aConstraintType, OdGeTol tol) ODRX_NOEXCEPT { int nConstraintCreated = 0; try { if (!DWGConstraintUtils::moduleIsLoaded()) { return eNotApplicable; } if (aEntId.empty() || aConstraintType.empty()) { return eOk; } /*OdDbObjectId consGrpId = DWGConstraintUtils::getConstraintGroup(DWGConstraintUtils::currentSpaceId(pDatabase), true); if (consGrpId.isNull()) { return eInvalidInput; } OdDbAssoc2dConstraintGroupPtr p2dConstrGrp = consGrpId.openObject(OdDb::kForWrite);*/ OdGeMatrix3d matUCS; DWGConstraintUtils::currentUCS(DWGConstraintUtils::currentSpaceId(pDatabase), nullptr, &matUCS); OdCsConstraintSystem system; // convert db to solver OdVector createResult; createSolverEntity(aEntId, matUCS, system.constraints(), createResult); for (const EntitySolverObjects& objects : createResult) { ODA_ASSERT_ONCE(!objects.aEntSolver.empty()); if (objects.aEntSolver[0]->type() == OdCs::kSpline) { const OdCsSplinePtr csSpline = objects.aEntSolver[0].dynamicCast(); const OdVector& apControlPoints = csSpline->controlPoints(); for (const auto& cp : apControlPoints) { system.geometries().push_back(cp); } system.geometries().push_back(csSpline); } else { for (const auto& ent : objects.aEntSolver) { system.geometries().push_back(ent); } } } // if need to save a replay //OdConstraintSolver solver(system); //solver.solve(); OdCsOverDefinedChecker checker; checker.addConstraintSystem(system); //OdCsFlowNetworkDotTracer tracer(system, *checker.m_pFlowNetwork); //tracer.save("c:/tmp/cs_trace.txt"); ODA_ASSERT_ONCE(checker.checkAndGetOverConstraints().isEmpty()); const unsigned int nSolverEnt = createResult.size(); for (auto type : aConstraintType) { int nArgs = -1; if (type == OdGeomConstraint::kHorizontal || type == OdGeomConstraint::kVertical) { nArgs = 1; } else if (type == OdGeomConstraint::kCoincident || type == OdGeomConstraint::kColinear || type == OdGeomConstraint::kParallel || type == OdGeomConstraint::kPerpendicular || type == OdGeomConstraint::kTangent || type == OdGeomConstraint::kConcentric || type == OdGeomConstraint::kEqualLength || type == OdGeomConstraint::kEqualRadius) { nArgs = 2; } else { continue; } if (nArgs == 1) { for (unsigned int i = 0; i < nSolverEnt; ++i) { const EntitySolverObjects& objects = createResult[i]; OdCsGeometryPtr ent = objects.aEntSolver[0]; OdCsConstraintPtr solverCnstr; switch (type) { case OdGeomConstraint::kHorizontal: solverCnstr = OdCsHorizontality::create(ent); break; case OdGeomConstraint::kVertical: solverCnstr = OdCsVerticality::create(ent); break; default: ODA_ASSERT(false); } if (!tryAddConstraint(system, solverCnstr, checker, tol)) { continue; } OdResult res = createHorizontalOrVerticalConstraint(pDatabase, objects.id, objects.ptEnt, type == OdGeomConstraint::kVertical); if (res != eOk) { ODA_ASSERT_ONCE(0); system.constraints().setLogicalLength(system.constraints().logicalLength() - 1); checker.removeConstraint(solverCnstr); } else { ++nConstraintCreated; } } } else // 2 args { if (type == OdGeomConstraint::kCoincident) { const double coincidentTol = 1e-10; OdSiSpatialIndexPtr indexCsEnt = OdSiSpatialIndex::createObject(OdSiSpatialIndex::kSiNoFlags, nSolverEnt, 30, 30); OdArray aSiEnt(nSolverEnt); for (const EntitySolverObjects& objects : createResult) { SolverSiEntity siEnt(objects); aSiEnt.emplace_back(siEnt); indexCsEnt->insert(&aSiEnt.last()); } OdGeMatrix3d matWcs = matUCS.inverse(); // find matches for all the points for (unsigned int i = 0; i < nSolverEnt; ++i) { OdVector& objectsI = createResult[i].aEntSolver; for (unsigned int iPt = 0; iPt < objectsI.size(); ++iPt) { OdCsPointPtr point = objectsI[iPt].dynamicCast(); if (point.isNull()) { continue; } SolverSiShape shape(point->position(), coincidentTol); PtEntityCoincidence finder(point->position(), matWcs, createResult[i].id, createResult[i].iPolySeg, coincidentTol); indexCsEnt->query(shape, finder); // point-point for (const EntitySolverObjects& ptCoinsident : finder.m_resultPoints) { ODA_ASSERT(point.get() != ptCoinsident.aEntSolver[0].get()); OdCsConstraintPtr solverCnstr = OdCsCoincidence::create(point, ptCoinsident.aEntSolver[0]); if (!tryAddConstraint(system, solverCnstr, checker, tol)) { continue; } OdResult res; if (createResult[i].id != ptCoinsident.id) { ODA_ASSERT_ONCE(createResult[i].iPolySeg < 0); // only same poly segs can have same ids OdGePoint3d point3d(point->position().x, point->position().y, 0.); point3d.transformBy(matWcs); res = createCoincidentConstraint(pDatabase, createResult[i].id, ptCoinsident.id, point3d, ptCoinsident.ptEnt, false); } else { const int iCurSeg = createResult[i].iPolySeg; ODA_ASSERT_ONCE(iCurSeg >= 0); // only same poly segs can have same ids OdDbSubentId subIdThis, subIdOther; subIdThis.setType(OdDb::kVertexSubentType); // index is 1-based subIdThis.setIndex(iPt == 1 ? iCurSeg + 1 : iCurSeg + 2); const int iOtherSeg = ptCoinsident.iPolySeg; ODA_ASSERT_ONCE(iOtherSeg >= 0); subIdOther.setType(OdDb::kVertexSubentType); subIdOther.setIndex(iOtherSeg + 1); OdDbFullSubentPath pathThis, pathOther; res = createCoincidentConstraint(pDatabase, createResult[i].id, ptCoinsident.id, OdDbFullSubentPath(ptCoinsident.id, subIdThis), OdDbFullSubentPath(ptCoinsident.id, subIdOther)); } if (res != eOk) { ODA_ASSERT_ONCE(0); system.constraints().setLogicalLength(system.constraints().logicalLength() - 1); checker.removeConstraint(solverCnstr); } else { ++nConstraintCreated; } } // point-curve for (const EntitySolverObjects& curveCoinsident : finder.m_resultCurves) { OdCsCoincidencePtr solverCnstr = OdCsCoincidence::create(curveCoinsident.aEntSolver[0], point); solverCnstr->setHelpParameter(curveCoinsident.ptEntParam); solverCnstr->setLockHelpParameter(true); if (!tryAddConstraint(system, solverCnstr, checker, tol)) { continue; } OdGePoint3d point3d(point->position().x, point->position().y, 0.); point3d.transformBy(matWcs); OdResult res = createCoincidentConstraint(pDatabase, curveCoinsident.id, createResult[i].id, curveCoinsident.ptEnt, point3d, true); if (res != eOk) { ODA_ASSERT_ONCE(0); system.constraints().setLogicalLength(system.constraints().logicalLength() - 1); checker.removeConstraint(solverCnstr); } else { ++nConstraintCreated; } } } } } else { for (unsigned int i = 0; i < nSolverEnt - 1; ++i) { OdVector& objectsI = createResult[i].aEntSolver; for (unsigned int j = i + 1; j < nSolverEnt; ++j) { OdVector& objectsJ = createResult[j].aEntSolver; OdCsConstraintPtr solverCnstr; switch (type) { case OdGeomConstraint::kParallel: solverCnstr = OdCsParallelism::create(objectsI[0], objectsJ[0]); break; case OdGeomConstraint::kPerpendicular: solverCnstr = OdCsPerpendicularity::create(objectsI[0], objectsJ[0]); break; case OdGeomConstraint::kTangent: solverCnstr = OdCsTangency::create(objectsI[0], objectsJ[0]); break; case OdGeomConstraint::kConcentric: solverCnstr = OdCsConcentric::create(objectsI[0], objectsJ[0]); break; case OdGeomConstraint::kEqualRadius: solverCnstr = OdCsEqualRadius::create(objectsI[0], objectsJ[0]); break; case OdGeomConstraint::kEqualLength: if (objectsI.logicalLength() < 3 || objectsJ.logicalLength() < 3 || objectsI[0]->type() != OdCs::kLine || objectsJ[0]->type() != OdCs::kLine) { continue; } solverCnstr = OdCsEqualDistance::create(objectsI[1], objectsI[2], objectsJ[1], objectsJ[2]); break; case OdGeomConstraint::kColinear: solverCnstr = OdCsCoincidence::create(objectsI[0], objectsJ[0]); break; default: ODA_ASSERT(false); } if (!tryAddConstraint(system, solverCnstr, checker, tol)) { continue; } const EntitySolverObjects& entI = createResult[i]; const EntitySolverObjects& entJ = createResult[j]; OdResult res = eOk; switch (type) { case OdGeomConstraint::kParallel: res = createParallelConstraint(pDatabase, entI.id, entJ.id, entI.ptEnt, entJ.ptEnt); break; case OdGeomConstraint::kColinear: res = createCollinearConstraint(pDatabase, entI.id, entJ.id, entI.ptEnt, entJ.ptEnt); break; case OdGeomConstraint::kPerpendicular: res = createPerpendicularConstraint(pDatabase, entI.id, entJ.id, entI.ptEnt, entJ.ptEnt); break; case OdGeomConstraint::kTangent: res = createTangentConstraint(pDatabase, entI.id, entJ.id, entI.ptEnt, entJ.ptEnt); break; case OdGeomConstraint::kConcentric: res = createConcentricConstraint(pDatabase, entI.id, entJ.id, entI.ptEnt, entJ.ptEnt); break; case OdGeomConstraint::kEqualLength: case OdGeomConstraint::kEqualRadius: res = createEqualConstraint(pDatabase, entI.id, entJ.id, entI.ptEnt, entJ.ptEnt); break; default: // constraint type is not supported ODA_ASSERT(false); } if (res != eOk) { ODA_ASSERT_ONCE(0); system.constraints().setLogicalLength(system.constraints().logicalLength() - 1); checker.removeConstraint(solverCnstr); } else { ++nConstraintCreated; } } } } } } } catch (const OdError& e) { return e.code(); } catch (...) { return eNotApplicable; } OdString iMsgName; iMsgName.format(L"--> Autoconstrain created %d constraints", nConstraintCreated); DWGConstraintsTrace(iMsgName); return eOk; }