X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchSolver%2FSketchSolver_Group.cpp;h=1ee8c9e3dfc9c86fe4d24c662120e3864b451b81;hb=3a60523c3fa0af9d8daf760fcf13f7f2c45836de;hp=dbe952f614cbe34bc0af9df929fdf3fbf96eeb02;hpb=3c445d389d3cbe4c98403bc62e4b6ba8d06cc314;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_Group.cpp b/src/SketchSolver/SketchSolver_Group.cpp index dbe952f61..1ee8c9e3d 100644 --- a/src/SketchSolver/SketchSolver_Group.cpp +++ b/src/SketchSolver/SketchSolver_Group.cpp @@ -6,42 +6,37 @@ #include "SketchSolver_Group.h" -#include #include #include +#include #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include +#include +#include -#include +#include +#include +#include +#include #include -#include +#include #include -#include +#include #include +#include +#include +#include #include #include -#include +#include #include #include -#include -#include -#include -#include -#include - #include #include @@ -51,9 +46,9 @@ class GroupIndexer { public: /// \brief Return vacant index - static Slvs_hGroup NEW_GROUP() { return ++myGroupIndex; } + static GroupID NEW_GROUP() { return ++myGroupIndex; } /// \brief Removes the index - static void REMOVE_GROUP(const Slvs_hGroup& theIndex) { + static void REMOVE_GROUP(const GroupID& theIndex) { if (myGroupIndex == theIndex) myGroupIndex--; } @@ -61,10 +56,27 @@ public: private: GroupIndexer() {}; - static Slvs_hGroup myGroupIndex; ///< index of the group + static GroupID myGroupIndex; ///< index of the group }; -Slvs_hGroup GroupIndexer::myGroupIndex = 0; +GroupID GroupIndexer::myGroupIndex = GID_OUTOFGROUP; + + +static void sendMessage(const char* theMessageName) +{ + std::shared_ptr aMessage = std::shared_ptr( + new Events_Message(Events_Loop::eventByName(theMessageName))); + Events_Loop::loop()->send(aMessage); +} + +static void sendMessage(const char* theMessageName, const std::set& theConflicting) +{ + std::shared_ptr aMessage = + std::shared_ptr( + new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName))); + aMessage->setObjects(theConflicting); + Events_Loop::loop()->send(aMessage); +} @@ -74,21 +86,23 @@ Slvs_hGroup GroupIndexer::myGroupIndex = 0; SketchSolver_Group::SketchSolver_Group( std::shared_ptr theWorkplane) - : myID(GroupIndexer::NEW_GROUP()) + : myID(GroupIndexer::NEW_GROUP()), + myPrevResult(STATUS_UNKNOWN) { // Initialize workplane - myWorkplaneID = SLVS_E_UNKNOWN; -#ifndef NDEBUG - assert(addWorkplane(theWorkplane)); -#else + myWorkplaneID = EID_UNKNOWN; addWorkplane(theWorkplane); -#endif } SketchSolver_Group::~SketchSolver_Group() { myConstraints.clear(); GroupIndexer::REMOVE_GROUP(myID); + // send the message that there is no more conflicting constraints + if (!myConflictingConstraints.empty()) { + sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints); + myConflictingConstraints.clear(); + } } // ============================================================================ @@ -106,65 +120,19 @@ bool SketchSolver_Group::isBaseWorkplane(CompositeFeaturePtr theWorkplane) const // Class: SketchSolver_Group // Purpose: verify are there any entities in the group used by given constraint // ============================================================================ -bool SketchSolver_Group::isInteract( - std::shared_ptr theFeature) const +bool SketchSolver_Group::isInteract(FeaturePtr theFeature) const { // Empty group interacts with everything - if (isEmpty()) return true; - ConstraintPtr aConstraint = std::dynamic_pointer_cast(theFeature); - if (aConstraint) - return myFeatureStorage->isInteract(aConstraint); - return myFeatureStorage->isInteract(std::dynamic_pointer_cast(theFeature)); -} - -// ============================================================================ -// Function: getFeatureId -// Class: SketchSolver_Group -// Purpose: Find the identifier of the feature, if it already exists in the group -// ============================================================================ -Slvs_hEntity SketchSolver_Group::getFeatureId(FeaturePtr theFeature) const -{ - Slvs_hEntity aResult = SLVS_E_UNKNOWN; - if (!myFeatureStorage) - return aResult; - std::set aConstraints = myFeatureStorage->getConstraints(theFeature); - if (aConstraints.empty()) - return aResult; - std::set::iterator aConstrIter = aConstraints.begin(); - for (; aConstrIter != aConstraints.end(); aConstrIter++) { - ConstraintConstraintMap::const_iterator aCIter = myConstraints.find(*aConstrIter); - if (aCIter == myConstraints.end()) - continue; - aResult = aCIter->second->getId(theFeature); - if (aResult != SLVS_E_UNKNOWN) - return aResult; - } - return SLVS_E_UNKNOWN; -} - -// ============================================================================ -// Function: getAttributeId -// Class: SketchSolver_Group -// Purpose: Find the identifier of the attribute, if it already exists in the group -// ============================================================================ -Slvs_hEntity SketchSolver_Group::getAttributeId(AttributePtr theAttribute) const -{ - Slvs_hEntity aResult = SLVS_E_UNKNOWN; - if (!myFeatureStorage) - return aResult; - std::set aConstraints = myFeatureStorage->getConstraints(theAttribute); - if (aConstraints.empty()) - return aResult; - std::set::iterator aConstrIter = aConstraints.begin(); - for (; aConstrIter != aConstraints.end(); aConstrIter++) { - ConstraintConstraintMap::const_iterator aCIter = myConstraints.find(*aConstrIter); - if (aCIter == myConstraints.end()) - continue; - aResult = aCIter->second->getId(theAttribute); - if (aResult != SLVS_E_UNKNOWN) - return aResult; - } - return SLVS_E_UNKNOWN; + if (isEmpty()) + return true; + // Check interaction with the storage + bool isInteracted = myStorage->isInteract(theFeature); + ConstraintConstraintMap::const_iterator anIt = myConstraints.begin(); + for (; !isInteracted && anIt != myConstraints.end(); ++anIt) + if (anIt->first->getKind() == SketchPlugin_MultiRotation::ID() || + anIt->first->getKind() == SketchPlugin_MultiTranslation::ID()) + isInteracted = anIt->second->isUsed(theFeature); + return isInteracted; } // ============================================================================ @@ -176,182 +144,140 @@ bool SketchSolver_Group::changeConstraint( std::shared_ptr theConstraint) { // There is no workplane yet, something wrong - if (myWorkplaneID == SLVS_E_UNKNOWN) + if (myWorkplaneID == EID_UNKNOWN) + return false; + + if (!theConstraint || !theConstraint->data()) return false; - if (!theConstraint) + if (!checkFeatureValidity(theConstraint)) return false; + BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); + myStorage->blockEvents(true); + bool isNewConstraint = myConstraints.find(theConstraint) == myConstraints.end(); if (isNewConstraint) { // Add constraint to the current group - SolverConstraintPtr aConstraint = - SketchSolver_Builder::getInstance()->createConstraint(theConstraint); + SolverConstraintPtr aConstraint = aBuilder->createConstraint(theConstraint); if (!aConstraint) return false; - aConstraint->setGroup(this); - aConstraint->setStorage(myStorage); + aConstraint->process(myStorage, getId(), getWorkplaneId()); if (!aConstraint->error().empty()) { if (aConstraint->error() == SketchSolver_Error::NOT_INITIALIZED()) return false; // some attribute are not initialized yet, don't show message Events_Error::send(aConstraint->error(), this); } - - // Additional verification of coincidence of several points - if (theConstraint->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { - ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); - for (; aCIter != myConstraints.end(); aCIter++) { - std::shared_ptr aCoincidence = - std::dynamic_pointer_cast(aCIter->second); - if (!aCoincidence) - continue; - std::shared_ptr aCoinc2 = - std::dynamic_pointer_cast(aConstraint); - if (aCoincidence != aCoinc2 && aCoincidence->isCoincide(aCoinc2)) { - aCoinc2->attach(aCoincidence); - // update other coincidences - ConstraintConstraintMap::iterator anIt = aCIter; - for (++anIt; anIt != myConstraints.end(); ++anIt) - if (anIt->second == aCIter->second) - anIt->second = aCoinc2; - aCIter->second = aCoinc2; - } - } - } myConstraints[theConstraint] = aConstraint; + + if (theConstraint->getKind() == SketchPlugin_ConstraintCoincidence::ID()) + notifyCoincidenceChanged(myConstraints[theConstraint]); } else myConstraints[theConstraint]->update(); - // Fix base features for fillet - if (isNewConstraint && theConstraint->getKind() == SketchPlugin_ConstraintFillet::ID()) { - std::list anAttrList = - theConstraint->data()->attributes(ModelAPI_AttributeRefAttr::typeId()); - std::list::iterator anAttrIter = anAttrList.begin(); - for (; anAttrIter != anAttrList.end(); anAttrIter++) { - AttributeRefAttrPtr aRefAttr = - std::dynamic_pointer_cast(*anAttrIter); - if (!aRefAttr || !aRefAttr->isObject()) - continue; - FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object()); - SolverConstraintPtr aConstraint = - SketchSolver_Builder::getInstance()->createRigidConstraint(aFeature); - if (!aConstraint) - continue; - aConstraint->setGroup(this); - aConstraint->setStorage(myStorage); - setTemporary(aConstraint); - } - } // Fix mirror line if (theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID()) { AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( theConstraint->attribute(SketchPlugin_ConstraintMirror::ENTITY_A())); if (aRefAttr && aRefAttr->isObject()) { - FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object()); + std::shared_ptr aFeature = + std::dynamic_pointer_cast( + ModelAPI_Feature::feature(aRefAttr->object())); if (aFeature) { - SolverConstraintPtr aConstraint = - SketchSolver_Builder::getInstance()->createRigidConstraint(aFeature); + SolverConstraintPtr aConstraint = aBuilder->createFixedConstraint(aFeature); if (aConstraint) { - aConstraint->setGroup(this); - aConstraint->setStorage(myStorage); + aConstraint->process(myStorage, getId(), getWorkplaneId()); setTemporary(aConstraint); } } } } - - if (!myFeatureStorage) - myFeatureStorage = FeatureStoragePtr(new SketchSolver_FeatureStorage); - myFeatureStorage->changeConstraint(theConstraint); - return true; } +// Update constraints if they contain specific feature +static void updateMultiConstraints(ConstraintConstraintMap& theConstraints, FeaturePtr theFeature) +{ + ConstraintConstraintMap::iterator aCIt = theConstraints.begin(); + for (; aCIt != theConstraints.end(); ++aCIt) { + SketchSolver_ConstraintType aType = aCIt->second->getType(); + if ((aType == CONSTRAINT_MULTI_ROTATION || + aType == CONSTRAINT_MULTI_TRANSLATION) + && aCIt->second->isUsed(theFeature)) + std::dynamic_pointer_cast(aCIt->second)->update(true); + else if ((aType == CONSTRAINT_TANGENT_CIRCLE_LINE || + aType == CONSTRAINT_SYMMETRIC || aType == CONSTRAINT_ANGLE) + && aCIt->second->isUsed(theFeature)) + aCIt->second->update(); + } +} -bool SketchSolver_Group::updateFeature(std::shared_ptr theFeature) +// Recalculate slave features of the Multi constraints +static void updateMultiConstraints(ConstraintConstraintMap& theConstraints) { - std::set aConstraints = - myFeatureStorage->getConstraints(std::dynamic_pointer_cast(theFeature)); - if (aConstraints.empty()) + ConstraintConstraintMap::iterator aCIt = theConstraints.begin(); + for (; aCIt != theConstraints.end(); ++aCIt) { + SketchSolver_ConstraintType aType = aCIt->second->getType(); + if ((aType == CONSTRAINT_MULTI_ROTATION || + aType == CONSTRAINT_MULTI_TRANSLATION)) + std::dynamic_pointer_cast(aCIt->second)->update(true); + } +} + +bool SketchSolver_Group::updateFeature(FeaturePtr theFeature) +{ + if (!checkFeatureValidity(theFeature)) return false; - std::set::iterator aCIter = aConstraints.begin(); - std::set aPostponed; // postponed constraints Multi-Rotation and Multi-Translation - for (; aCIter != aConstraints.end(); aCIter++) { - ConstraintConstraintMap::iterator aSolConIter = myConstraints.find(*aCIter); - if (aSolConIter == myConstraints.end() || !aSolConIter->first->data() || - !aSolConIter->first->data()->isValid()) - continue; - myFeatureStorage->changeFeature(theFeature, aSolConIter->first); - if (aSolConIter->first->getKind() == SketchPlugin_MultiRotation::ID() || - aSolConIter->first->getKind() == SketchPlugin_MultiTranslation::ID()) { - aPostponed.insert(aSolConIter->second); - continue; - } - aSolConIter->second->addFeature(theFeature); - aSolConIter->second->update(); - } + bool isBlocked = myStorage->isEventsBlocked(); + if (!isBlocked) + myStorage->blockEvents(true); - // Update postponed constraints - std::set::iterator aSCIter = aPostponed.begin(); - for (; aSCIter != aPostponed.end(); ++aSCIter) { - (*aSCIter)->addFeature(theFeature); - (*aSCIter)->update(); - } - return true; + myStorage->refresh(true); + bool isUpdated = myStorage->update(theFeature); + + updateMultiConstraints(myConstraints, theFeature); + + // events were not blocked before, the feature has not been updated, + // so it is necessary to revert blocking + if (!isUpdated && !isBlocked) + myStorage->blockEvents(false); + return isUpdated; } -void SketchSolver_Group::moveFeature(std::shared_ptr theFeature) +void SketchSolver_Group::moveFeature(FeaturePtr theFeature) { - // Firstly, create temporary rigid constraint - SolverConstraintPtr aConstraint = - SketchSolver_Builder::getInstance()->createMovementConstraint(theFeature); + BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); + + // Firstly, revert changes in the fixed entities + myStorage->blockEvents(true); + myStorage->refresh(true); + + // Then, create temporary Fixed constraint + SolverConstraintPtr aConstraint = aBuilder->createMovementConstraint(theFeature); if (!aConstraint) return; - aConstraint->setGroup(this); - aConstraint->setStorage(myStorage); + aConstraint->process(myStorage, getId(), getWorkplaneId()); if (aConstraint->error().empty()) setTemporary(aConstraint); - // Secondly, update the feature - updateFeature(theFeature); -} - -// ============================================================================ -// Function: fixFeaturesList -// Class: SketchSolver_Group -// Purpose: Apply temporary rigid constraints for the list of features -// ============================================================================ -void SketchSolver_Group::fixFeaturesList(AttributeRefListPtr theList) -{ - std::list aList = theList->list(); - std::list::iterator anIt = aList.begin(); - std::list aFeatures; - // Sort features, at begining there are features used by Equal constraint - for (; anIt != aList.end(); anIt++) { - if (!(*anIt)) - continue; - FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt); - std::set aConstraints = myFeatureStorage->getConstraints(aFeature); - std::set::iterator aCIter = aConstraints.begin(); - for (; aCIter != aConstraints.end(); aCIter++) - if ((*aCIter)->getKind() == SketchPlugin_ConstraintEqual::ID()) - break; - if (aCIter != aConstraints.end()) - aFeatures.push_front(aFeature); - else - aFeatures.push_back(aFeature); - } - std::list::iterator aFeatIter = aFeatures.begin(); - for (; aFeatIter != aFeatures.end(); aFeatIter++) { - SolverConstraintPtr aConstraint = - SketchSolver_Builder::getInstance()->createRigidConstraint(*aFeatIter); - if (!aConstraint) - continue; - aConstraint->setGroup(this); - aConstraint->setStorage(myStorage); - setTemporary(aConstraint); + // Secondly, search attributes of the feature in the list of the Multi constraints and update them + updateMultiConstraints(myConstraints, theFeature); + + // Workaround to process arcs. + // When move unconstrained arc, add temporary constraint to fix radius. + if (theFeature->getKind() == SketchPlugin_Arc::ID()) { + bool hasDup = myStorage->hasDuplicatedConstraint(); + SolverConstraintPtr aFixedRadius = aBuilder->createFixedArcRadiusConstraint(theFeature); + if (aFixedRadius) { + aFixedRadius->process(myStorage, getId(), getWorkplaneId()); + hasDup = myStorage->hasDuplicatedConstraint() && !hasDup; + if (aFixedRadius->error().empty() && !hasDup) + setTemporary(aFixedRadius); + else + aFixedRadius->remove(); + } } } @@ -362,11 +288,14 @@ void SketchSolver_Group::fixFeaturesList(AttributeRefListPtr theList) // ============================================================================ bool SketchSolver_Group::addWorkplane(CompositeFeaturePtr theSketch) { - if (myWorkplaneID != SLVS_E_UNKNOWN || theSketch->getKind() != SketchPlugin_Sketch::ID()) + if (myWorkplaneID != EID_UNKNOWN || theSketch->getKind() != SketchPlugin_Sketch::ID()) return false; // the workplane already exists or the function parameter is not Sketch mySketch = theSketch; - updateWorkplane(); + if (!updateWorkplane()) { + mySketch = CompositeFeaturePtr(); + return false; + } return true; } @@ -377,55 +306,17 @@ bool SketchSolver_Group::addWorkplane(CompositeFeaturePtr theSketch) // ============================================================================ bool SketchSolver_Group::updateWorkplane() { + BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); if (!myStorage) // Create storage if not exists - myStorage = StoragePtr(new SketchSolver_Storage); - SketchSolver_Builder* aBuilder = SketchSolver_Builder::getInstance(); - - std::vector anEntities; - std::vector aParams; - if (!aBuilder->createWorkplane(mySketch, anEntities, aParams)) - return false; + myStorage = aBuilder->createStorage(getId()); - if (myWorkplaneID == SLVS_E_UNKNOWN) { - myWorkplaneID = anEntities.back().h; - // Add new workplane elements - std::vector::iterator aParIter = aParams.begin(); - for (; aParIter != aParams.end(); aParIter++) { - aParIter->h = SLVS_E_UNKNOWN; // the ID should be generated by storage - aParIter->group = myID; - aParIter->h = myStorage->addParameter(*aParIter); - } - std::vector::iterator anEntIter = anEntities.begin(); - for (; anEntIter != anEntities.end(); anEntIter++) { - anEntIter->h = SLVS_E_UNKNOWN; // the ID should be generated by storage - anEntIter->group = myID; - anEntIter->wrkpl = myWorkplaneID; - for (int i = 0; i < 4; i++) - if (anEntIter->param[i] != SLVS_E_UNKNOWN) - anEntIter->param[i] = aParams[anEntIter->param[i]-1].h; - for (int i = 0; i < 4; i++) - if (anEntIter->point[i] != SLVS_E_UNKNOWN) - anEntIter->point[i] = anEntities[anEntIter->point[i]-1].h; - anEntIter->h = myStorage->addEntity(*anEntIter); - } - } else { - // Update existent workplane - const Slvs_Entity& aWP = myStorage->getEntity(myWorkplaneID); - const Slvs_Entity& anOrigin = myStorage->getEntity(aWP.point[0]); - const Slvs_Entity& aNormal = myStorage->getEntity(aWP.normal); - // Get parameters and update them - Slvs_hParam aWPParams[7] = { - anOrigin.param[0], anOrigin.param[1], anOrigin.param[2], - aNormal.param[0], aNormal.param[1], aNormal.param[2], aNormal.param[3] - }; - std::vector::iterator aParIter = aParams.begin(); - for (int i = 0; aParIter != aParams.end(); aParIter++, i++) { - Slvs_Param aParam = myStorage->getParameter(aWPParams[i]); - aParam.val = aParIter->val; - myStorage->updateParameter(aParam); - } + // sketch should be unchanged, set it out of current group + bool isUpdated = myStorage->update(FeaturePtr(mySketch), GID_OUTOFGROUP); + if (isUpdated) { + EntityWrapperPtr anEntity = myStorage->entity(FeaturePtr(mySketch)); + myWorkplaneID = anEntity->id(); } - return myWorkplaneID > 0; + return isUpdated; } // ============================================================================ @@ -436,50 +327,103 @@ bool SketchSolver_Group::updateWorkplane() bool SketchSolver_Group::resolveConstraints() { bool aResolved = false; - if (myStorage->isNeedToResolve() && !isEmpty()) { - myConstrSolver.setGroupID(myID); - myStorage->initializeSolver(myConstrSolver); + bool isGroupEmpty = isEmpty() && myStorage->isEmpty(); + if (myStorage->isNeedToResolve() && !isGroupEmpty) { + if (!mySketchSolver) + mySketchSolver = SketchSolver_Manager::instance()->builder()->createSolver(); - int aResult = SLVS_RESULT_OKAY; + mySketchSolver->setGroup(myID); + mySketchSolver->calculateFailedConstraints(false); + myStorage->initializeSolver(mySketchSolver); + mySketchSolver->prepare(); + + SketchSolver_SolveStatus aResult = STATUS_OK; try { if (myStorage->hasDuplicatedConstraint()) - aResult = SLVS_RESULT_INCONSISTENT; + aResult = STATUS_INCONSISTENT; else { // To avoid overconstraint situation, we will remove temporary constraints one-by-one // and try to find the case without overconstraint bool isLastChance = false; - int aNbTemp = myStorage->numberTemporary(); while (true) { - aResult = myConstrSolver.solve(); - if (aResult == SLVS_RESULT_OKAY || isLastChance) + aResult = mySketchSolver->solve(); + if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET || isLastChance) break; - if (aNbTemp <= 0) { - // try to update parameters and resolve once again - ConstraintConstraintMap::iterator aConstrIt = myConstraints.begin(); - for (; aConstrIt != myConstraints.end(); ++aConstrIt) - aConstrIt->second->update(); - isLastChance = true; - } else - aNbTemp = myStorage->deleteTemporaryConstraint(); - myStorage->initializeSolver(myConstrSolver); +//// // try to update parameters and resolve once again +//// ConstraintConstraintMap::iterator aConstrIt = myConstraints.begin(); +//// for (; aConstrIt != myConstraints.end(); ++aConstrIt) +//// aConstrIt->second->update(); + isLastChance = true; + + removeTemporaryConstraints(); + mySketchSolver->calculateFailedConstraints(true); // something failed => need to find it + myStorage->initializeSolver(mySketchSolver); } } } catch (...) { - Events_Error::send(SketchSolver_Error::SOLVESPACE_CRASH(), this); +// Events_Error::send(SketchSolver_Error::SOLVESPACE_CRASH(), this); + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::SOLVESPACE_CRASH()); + Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + if (myPrevResult == STATUS_OK || myPrevResult == STATUS_UNKNOWN) { + // the error message should be changed before sending the message + sendMessage(EVENT_SOLVER_FAILED); + myPrevResult = STATUS_FAILED; + } + mySketchSolver->undo(); return false; } - if (aResult == SLVS_RESULT_OKAY) { // solution succeeded, store results into correspondent attributes - myFeatureStorage->blockEvents(true); - ConstraintConstraintMap::iterator aConstrIter = myConstraints.begin(); - for (; aConstrIter != myConstraints.end(); aConstrIter++) - aConstrIter->second->refresh(); - myFeatureStorage->blockEvents(false); - } else if (!myConstraints.empty()) - Events_Error::send(SketchSolver_Error::CONSTRAINTS(), this); + if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET) { // solution succeeded, store results into correspondent attributes + myStorage->refresh(); + updateMultiConstraints(myConstraints); + if (myPrevResult != STATUS_OK || myPrevResult == STATUS_UNKNOWN) { + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(""); + Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + // the error message should be changed before sending the message + sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints); + myConflictingConstraints.clear(); + myPrevResult = STATUS_OK; + } + } else { + mySketchSolver->undo(); + if (!myConstraints.empty()) { + // the error message should be changed before sending the message + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::CONSTRAINTS()); + Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + if (myPrevResult != aResult || myPrevResult == STATUS_UNKNOWN) { + // Obtain list of conflicting constraints + std::set aConflicting = myStorage->getConflictingConstraints(mySketchSolver); + + if (myConflictingConstraints.empty()) + sendMessage(EVENT_SOLVER_FAILED, aConflicting); + else { + std::set::iterator anIt = aConflicting.begin(); + for (; anIt != aConflicting.end(); ++anIt) + myConflictingConstraints.erase(*anIt); + if (!myConflictingConstraints.empty()) { + // some constraints does not conflict, send corresponding message + sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints); + } + } + myConflictingConstraints = aConflicting; + myPrevResult = aResult; + } + } + } aResolved = true; + } else if (!isGroupEmpty) { + // Check there are constraints Fixed. If they exist, update parameters by stored values + ConstraintConstraintMap::iterator aCIt = myConstraints.begin(); + for (; aCIt != myConstraints.end(); ++aCIt) + if (aCIt->first->getKind() == SketchPlugin_ConstraintRigid::ID()) { + aResolved = true; + break; + } + if (aCIt != myConstraints.end()) + myStorage->refresh(); } removeTemporaryConstraints(); + myStorage->blockEvents(false); myStorage->setNeedToResolve(false); return aResolved; } @@ -494,21 +438,20 @@ void SketchSolver_Group::mergeGroups(const SketchSolver_Group& theGroup) // If specified group is empty, no need to merge if (theGroup.isEmpty()) return; - if (!myFeatureStorage) - myFeatureStorage = FeatureStoragePtr(new SketchSolver_FeatureStorage); - std::vector aComplexConstraints; + std::set aConstraints; ConstraintConstraintMap::const_iterator aConstrIter = theGroup.myConstraints.begin(); - // append simple constraints for (; aConstrIter != theGroup.myConstraints.end(); aConstrIter++) - if (isComplexConstraint(aConstrIter->first)) - aComplexConstraints.push_back(aConstrIter->first); - else - changeConstraint(aConstrIter->first); - // append complex constraints - std::vector::iterator aComplexIter = aComplexConstraints.begin(); - for (; aComplexIter != aComplexConstraints.end(); aComplexIter++) - changeConstraint(*aComplexIter); + aConstraints.insert(aConstrIter->first); + + std::list aSortedConstraints = selectApplicableFeatures(aConstraints); + std::list::iterator aSCIter = aSortedConstraints.begin(); + for (; aSCIter != aSortedConstraints.end(); ++aSCIter) { + ConstraintPtr aConstr = std::dynamic_pointer_cast(*aSCIter); + if (!aConstr) + continue; + changeConstraint(aConstr); + } } // ============================================================================ @@ -516,41 +459,41 @@ void SketchSolver_Group::mergeGroups(const SketchSolver_Group& theGroup) // Class: SketchSolver_Group // Purpose: divide the group into several subgroups // ============================================================================ -void SketchSolver_Group::splitGroup(std::vector& theCuts) +void SketchSolver_Group::splitGroup(std::list& theCuts) { + // New storage will be used in trimmed way to store the list of constraint interacted together. + StoragePtr aNewStorage = SketchSolver_Manager::instance()->builder()->createStorage(getId()); + std::list aDummyVec; // empty vector to avoid creation of solver's constraints + // Obtain constraints, which should be separated - FeatureStoragePtr aNewFeatStorage(new SketchSolver_FeatureStorage); - std::vector anUnusedConstraints; + std::list anUnusedConstraints; ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); for ( ; aCIter != myConstraints.end(); aCIter++) { - std::list aBaseConstraints = aCIter->second->constraints(); - std::list::iterator anIter = aBaseConstraints.begin(); - for (; anIter != aBaseConstraints.end(); anIter++) - if (aNewFeatStorage->isInteract(*anIter)) { - aNewFeatStorage->changeConstraint(*anIter); - } else - anUnusedConstraints.push_back(*anIter); + if (aNewStorage->isInteract(FeaturePtr(aCIter->first))) + aNewStorage->addConstraint(aCIter->first, aDummyVec); + else + anUnusedConstraints.push_back(aCIter->first); } // Check the unused constraints once again, because they may become interacted with new storage since adding constraints - std::vector::iterator aUnuseIt = anUnusedConstraints.begin(); + std::list::iterator aUnuseIt = anUnusedConstraints.begin(); while (aUnuseIt != anUnusedConstraints.end()) { - if (aNewFeatStorage->isInteract(*aUnuseIt)) { - size_t aShift = aUnuseIt - anUnusedConstraints.begin(); + if (aNewStorage->isInteract(FeaturePtr(*aUnuseIt))) { + aNewStorage->addConstraint(*aUnuseIt, aDummyVec); anUnusedConstraints.erase(aUnuseIt); - aUnuseIt = anUnusedConstraints.begin() + aShift; + aUnuseIt = anUnusedConstraints.begin(); continue; } aUnuseIt++; } - std::vector::iterator aCutsIter; + std::list::iterator aCutsIter; aUnuseIt = anUnusedConstraints.begin(); - for ( ; aUnuseIt != anUnusedConstraints.end(); aUnuseIt++) { + for ( ; aUnuseIt != anUnusedConstraints.end(); ++aUnuseIt) { // Remove unused constraints removeConstraint(*aUnuseIt); // Try to append constraint to already existent group - for (aCutsIter = theCuts.begin(); aCutsIter != theCuts.end(); aCutsIter++) + for (aCutsIter = theCuts.begin(); aCutsIter != theCuts.end(); ++aCutsIter) if ((*aCutsIter)->isInteract(*aUnuseIt)) { (*aCutsIter)->changeConstraint(*aUnuseIt); break; @@ -560,6 +503,15 @@ void SketchSolver_Group::splitGroup(std::vector& theCuts) SketchSolver_Group* aGroup = new SketchSolver_Group(mySketch); aGroup->changeConstraint(*aUnuseIt); theCuts.push_back(aGroup); + } else { + // Find other groups interacting with constraint + std::list::iterator aBaseGroupIt = aCutsIter; + for (++aCutsIter; aCutsIter != theCuts.end(); ++aCutsIter) + if ((*aCutsIter)->isInteract(*aUnuseIt)) { + (*aBaseGroupIt)->mergeGroups(**aCutsIter); + std::list::iterator aRemoveIt = aCutsIter--; + theCuts.erase(aRemoveIt); + } } } } @@ -571,28 +523,35 @@ void SketchSolver_Group::splitGroup(std::vector& theCuts) // ============================================================================ bool SketchSolver_Group::isConsistent() { - if (!myFeatureStorage) // no one constraint is initialized yet + if (isEmpty()) // no one constraint is initialized yet return true; - bool aResult = myFeatureStorage->isConsistent(); + // Check the features and constraint is the storage are valid + bool aResult = myStorage->isConsistent(); + if (aResult) { + // additional check of consistency of the Fixed constraint, + // because they are not added to the storage + ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); + for (; aCIter != myConstraints.end(); ++aCIter) + if (aCIter->first->getKind() == SketchPlugin_ConstraintRigid::ID() && + (!aCIter->first->data() || !aCIter->first->data()->isValid())) { + aResult = false; + break; + } + } if (!aResult) { - // remove invalid entities + // remove invalid constraints + std::set anInvalidConstraints; ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); - while (aCIter != myConstraints.end()) { - std::list aConstraints = aCIter->second->constraints(); - std::list::iterator anIt = aConstraints.begin(); - for (; anIt != aConstraints.end(); anIt++) - if (!(*anIt)->data() || !(*anIt)->data()->isValid()) - if (aCIter->second->remove(*anIt)) { - // the constraint is fully removed, detach it from the list - ConstraintConstraintMap::iterator aTmpIt = aCIter++; - myFeatureStorage->removeConstraint(aTmpIt->first); - myConstraints.erase(aTmpIt); - break; - } - if (anIt == aConstraints.end()) - aCIter++; + for (; aCIter != myConstraints.end(); ++aCIter) { + if (!aCIter->first->data() || !aCIter->first->data()->isValid()) + anInvalidConstraints.insert(aCIter->first); } + std::set::const_iterator aRemoveIt = anInvalidConstraints.begin(); + for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt) + removeConstraint(*aRemoveIt); + // remove invalid features + myStorage->removeInvalidEntities(); } return aResult; } @@ -605,15 +564,14 @@ bool SketchSolver_Group::isConsistent() // ============================================================================ void SketchSolver_Group::removeTemporaryConstraints() { - myTempConstraints.clear(); - while (myStorage->numberTemporary()) - myStorage->deleteTemporaryConstraint(); - // Clean lists of removed entities in the storage - std::set aRemPar; - std::set aRemEnt; - std::set aRemCon; - myStorage->getRemoved(aRemPar, aRemEnt, aRemCon); + std::set::iterator aTmpIt = myTempConstraints.begin(); + for (; aTmpIt != myTempConstraints.end(); ++aTmpIt) + (*aTmpIt)->remove(); + + if (!myTempConstraints.empty()) + myStorage->verifyFixed(); myStorage->setNeedToResolve(false); + myTempConstraints.clear(); } // ============================================================================ @@ -623,12 +581,12 @@ void SketchSolver_Group::removeTemporaryConstraints() // ============================================================================ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint) { - myFeatureStorage->removeConstraint(theConstraint); ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); for (; aCIter != myConstraints.end(); aCIter++) - if (aCIter->second->hasConstraint(theConstraint)) { - if (!aCIter->second->remove(theConstraint)) // the constraint is not fully removed - aCIter = myConstraints.end(); + if (aCIter->first == theConstraint) { + aCIter->second->remove(); // the constraint is not fully removed + if (aCIter->first->getKind() == SketchPlugin_ConstraintCoincidence::ID()) + notifyCoincidenceChanged(aCIter->second); break; } if (aCIter != myConstraints.end()) @@ -636,25 +594,119 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint) } // ============================================================================ -// Function: isComplexConstraint +// Function: setTemporary // Class: SketchSolver_Group -// Purpose: verifies the constraint is complex, i.e. it needs another constraints to be created before +// Purpose: append given constraint to the group of temporary constraints // ============================================================================ -bool SketchSolver_Group::isComplexConstraint(FeaturePtr theConstraint) +void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint) { - return theConstraint->getKind() == SketchPlugin_ConstraintFillet::ID() || - theConstraint->getKind() == SketchPlugin_ConstraintMirror::ID() || - theConstraint->getKind() == SketchPlugin_ConstraintTangent::ID(); + myTempConstraints.insert(theConstraint); } + // ============================================================================ -// Function: setTemporary +// Function: checkFeatureValidity // Class: SketchSolver_Group -// Purpose: append given constraint to th group of temporary constraints +// Purpose: verifies is the feature valid // ============================================================================ -void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint) +bool SketchSolver_Group::checkFeatureValidity(FeaturePtr theFeature) { - theConstraint->makeTemporary(); - myTempConstraints.insert(theConstraint); + if (!theFeature || !theFeature->data()->isValid()) + return true; + + SessionPtr aMgr = ModelAPI_Session::get(); + ModelAPI_ValidatorsFactory* aFactory = aMgr->validators(); + return aFactory->validate(theFeature); } + + + +// =========== Auxiliary functions ======================================== +static double featureToVal(FeaturePtr theFeature) +{ + if (theFeature->getKind() == SketchPlugin_Sketch::ID()) + return 0.0; // sketch + ConstraintPtr aConstraint = std::dynamic_pointer_cast(theFeature); + if (!aConstraint) + return 1.0; // features (arc, circle, line, point) + + const std::string& anID = aConstraint->getKind(); + if (anID == SketchPlugin_ConstraintCoincidence::ID()) { + AttributeRefAttrPtr anAttrA = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); + AttributeRefAttrPtr anAttrB = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); + if (anAttrA && anAttrB && (anAttrA->isObject() || anAttrB->isObject())) + return 2.0; // point-on-line and point-on-circle should go before points coincidence constraint + return 2.5; + } + if (anID == SketchPlugin_ConstraintDistance::ID() || + anID == SketchPlugin_ConstraintLength::ID() || + anID == SketchPlugin_ConstraintRadius::ID()) + return 3.0; + if (anID == SketchPlugin_ConstraintAngle::ID()) + return 3.5; + if (anID == SketchPlugin_ConstraintHorizontal::ID() || + anID == SketchPlugin_ConstraintVertical::ID() || + anID == SketchPlugin_ConstraintParallel::ID() || + anID == SketchPlugin_ConstraintPerpendicular::ID()) + return 4.0; + if (anID == SketchPlugin_ConstraintEqual::ID()) + return 5.0; + if (anID == SketchPlugin_ConstraintTangent::ID() || + anID == SketchPlugin_ConstraintMirror::ID()) + return 6.0; + if (anID == SketchPlugin_ConstraintRigid::ID()) + return 7.0; + if (anID == SketchPlugin_MultiRotation::ID() || + anID == SketchPlugin_MultiTranslation::ID()) + return 8.0; + + // all other constraints are placed between Equal and Tangent constraints + return 5.5; +} + +static bool isLess(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + return featureToVal(theFeature1) < featureToVal(theFeature2); +} + +std::list SketchSolver_Group::selectApplicableFeatures(const std::set& theObjects) +{ + std::list aResult; + std::list::iterator aResIt; + + std::set::const_iterator anObjIter = theObjects.begin(); + for (; anObjIter != theObjects.end(); ++anObjIter) { + // Operate sketch itself and SketchPlugin features only. + // Also, the Fillet need to be skipped, because there are several separated constraints composing it. + FeaturePtr aFeature = std::dynamic_pointer_cast(*anObjIter); + if (!aFeature) + continue; + std::shared_ptr aSketchFeature = + std::dynamic_pointer_cast(aFeature); + if ((aFeature->getKind() != SketchPlugin_Sketch::ID() && !aSketchFeature) || + aFeature->getKind() == SketchPlugin_ConstraintFillet::ID()) + continue; + + // Find the place where to insert a feature + for (aResIt = aResult.begin(); aResIt != aResult.end(); ++aResIt) + if (isLess(aFeature, *aResIt)) + break; + aResult.insert(aResIt, aFeature); + } + + return aResult; +} + +void SketchSolver_Group::notifyCoincidenceChanged(SolverConstraintPtr theCoincidence) +{ + const std::list& aCoincident = theCoincidence->attributes(); + EntityWrapperPtr anAttr1 = aCoincident.front(); + EntityWrapperPtr anAttr2 = aCoincident.back(); + + ConstraintConstraintMap::iterator anIt = myConstraints.begin(); + for (; anIt != myConstraints.end(); ++anIt) + anIt->second->notifyCoincidenceChanged(anAttr1, anAttr2); +}