X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchSolver%2FSketchSolver_Group.cpp;h=ab9b38fc2df6789e841b275e22e425f2805d9e19;hb=9f3145344ab3485d62f8cf822212581d91b435a2;hp=34fb4d17b525179faf294141fff76a7bb9c23f5e;hpb=d73b65e0b6e8c38fc7fdcbd302e69b51db4082d8;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_Group.cpp b/src/SketchSolver/SketchSolver_Group.cpp index 34fb4d17b..ab9b38fc2 100644 --- a/src/SketchSolver/SketchSolver_Group.cpp +++ b/src/SketchSolver/SketchSolver_Group.cpp @@ -1,117 +1,98 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D - -// File: SketchSolver_Group.cpp -// Created: 27 May 2014 -// Author: Artem ZHIDKOV +// Copyright (C) 2014-2019 CEA/DEN, EDF R&D +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// #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 -/// \brief This class is used to give unique index to the groups -class GroupIndexer +static void sendMessage(const char* theMessageName) { -public: - /// \brief Return vacant index - static GroupID NEW_GROUP() { return ++myGroupIndex; } - /// \brief Removes the index - static void REMOVE_GROUP(const GroupID& theIndex) { - if (myGroupIndex == theIndex) - myGroupIndex--; - } - -private: - GroupIndexer() {}; + std::shared_ptr aMessage = std::shared_ptr( + new Events_Message(Events_Loop::eventByName(theMessageName))); + Events_Loop::loop()->send(aMessage); +} - static GroupID myGroupIndex; ///< index of the group -}; +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); +} -GroupID GroupIndexer::myGroupIndex = GID_OUTOFGROUP; +static void sendMessage(const char* theMessageName, + const CompositeFeaturePtr& theSketch, + const int theDOF) +{ + std::shared_ptr aMessage = + std::shared_ptr( + new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName))); + std::set anObjects; + anObjects.insert(theSketch); + aMessage->setObjects(anObjects); + aMessage->dof(theDOF); -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); } // ======================================================== -// ========= SketchSolver_Group =============== +// ========= SketchSolver_Group =============== // ======================================================== -SketchSolver_Group::SketchSolver_Group( - std::shared_ptr theWorkplane) - : myID(GroupIndexer::NEW_GROUP()), - myPrevResult(STATUS_UNKNOWN) +SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane) + : myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN), + myDOF(-1), + myIsEventsBlocked(false), + myMultiConstraintUpdateStack(0) { - // Initialize workplane - myWorkplaneID = EID_UNKNOWN; - addWorkplane(theWorkplane); + mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver); + myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver)); + updateSketch(theWorkplane); } SketchSolver_Group::~SketchSolver_Group() { myConstraints.clear(); - GroupIndexer::REMOVE_GROUP(myID); -} - -// ============================================================================ -// Function: isBaseWorkplane -// Class: SketchSolver_Group -// Purpose: verify the group is based on the given workplane -// ============================================================================ -bool SketchSolver_Group::isBaseWorkplane(CompositeFeaturePtr theWorkplane) const -{ - return theWorkplane == mySketch; -} - -// ============================================================================ -// Function: isInteract -// Class: SketchSolver_Group -// Purpose: verify are there any entities in the group used by given constraint -// ============================================================================ -bool SketchSolver_Group::isInteract(FeaturePtr theFeature) const -{ - // Empty group interacts with everything - if (isEmpty()) - return true; - // Check interaction with the storage - return myStorage->isInteract(theFeature); + // send the message that there is no more conflicting constraints + if (!myConflictingConstraints.empty()) { + sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints); + myConflictingConstraints.clear(); + } } // ============================================================================ @@ -122,144 +103,113 @@ bool SketchSolver_Group::isInteract(FeaturePtr theFeature) const bool SketchSolver_Group::changeConstraint( std::shared_ptr theConstraint) { - // There is no workplane yet, something wrong - if (myWorkplaneID == EID_UNKNOWN) - return false; - - if (!theConstraint || !theConstraint->data()) - return false; - - 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 = aBuilder->createConstraint(theConstraint); + SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createConstraint(theConstraint); if (!aConstraint) return false; - aConstraint->process(myStorage, getId(), getWorkplaneId()); + aConstraint->process(myStorage, myIsEventsBlocked); 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); + Events_InfoMessage("SketchSolver_Group", aConstraint->error(), this).send(); } myConstraints[theConstraint] = aConstraint; } else myConstraints[theConstraint]->update(); - // 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()) { - std::shared_ptr aFeature = - std::dynamic_pointer_cast( - ModelAPI_Feature::feature(aRefAttr->object())); - if (aFeature) { - SolverConstraintPtr aConstraint = aBuilder->createFixedConstraint(aFeature); - if (aConstraint) { - aConstraint->process(myStorage, getId(), getWorkplaneId()); - setTemporary(aConstraint); - } - } - } - } + // constraint is created/updated => reset stack of "multi" constraints updates + myMultiConstraintUpdateStack = 0; return true; } -static void updateMultiConstraints(ConstraintConstraintMap& theConstraints, FeaturePtr theFeature) +bool SketchSolver_Group::updateSketch(CompositeFeaturePtr theSketch) { - ConstraintConstraintMap::iterator aCIt = theConstraints.begin(); - for (; aCIt != theConstraints.end(); ++aCIt) { - if ((aCIt->second->getType() == CONSTRAINT_MULTI_ROTATION || - aCIt->second->getType() == CONSTRAINT_MULTI_TRANSLATION) - && aCIt->second->isUsed(theFeature)) - std::dynamic_pointer_cast(aCIt->second)->update(true); + static const double THE_TOLERANCE = 1.e-7; + bool isChanged = theSketch != mySketch; + + AttributePointPtr anOrigin = std::dynamic_pointer_cast( + theSketch->attribute(SketchPlugin_Sketch::ORIGIN_ID())); + AttributeDirPtr aNorm = std::dynamic_pointer_cast( + theSketch->attribute(SketchPlugin_Sketch::NORM_ID())); + AttributeDirPtr aDirX = std::dynamic_pointer_cast( + theSketch->attribute(SketchPlugin_Sketch::DIRX_ID())); + + isChanged = isChanged + || (mySketchOrigin && anOrigin && anOrigin->pnt()->distance(mySketchOrigin) > THE_TOLERANCE) + || (mySketchNormal && aNorm && aNorm->xyz()->distance(mySketchNormal->xyz()) > THE_TOLERANCE) + || (mySketchXDir && aDirX && aDirX->xyz()->distance(mySketchXDir->xyz()) > THE_TOLERANCE); + + if (isChanged) { + mySketch = theSketch; + mySketchOrigin = anOrigin->pnt(); + mySketchNormal = aNorm->dir(); + mySketchXDir = aDirX->dir(); + + myStorage->notify(theSketch); } + return true; } bool SketchSolver_Group::updateFeature(FeaturePtr theFeature) { - if (!checkFeatureValidity(theFeature)) - return false; - - bool isBlocked = myStorage->isEventsBlocked(); - if (!isBlocked) - myStorage->blockEvents(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; + return myStorage->update(theFeature); } -void SketchSolver_Group::moveFeature(FeaturePtr theFeature) +template +static SolverConstraintPtr move(StoragePtr theStorage, + SolverPtr theSketchSolver, + int theSketchDOF, + bool theEventsBlocked, + Type theFeatureOrPoint, + const std::shared_ptr& theFrom, + const std::shared_ptr& theTo) { - BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); - - // Firstly, revert changes in the fixed entities - myStorage->blockEvents(true); - myStorage->refresh(true); + bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0); + if (theSketchDOF == 0 && isEntityExists) { + // avoid moving elements of fully constrained sketch + theStorage->refresh(); + return SolverConstraintPtr(); + } - // Then, create temporary Fixed constraint - SolverConstraintPtr aConstraint = aBuilder->createMovementConstraint(theFeature); - if (!aConstraint) - return; - aConstraint->process(myStorage, getId(), getWorkplaneId()); - if (aConstraint->error().empty()) - setTemporary(aConstraint); + // Create temporary Fixed constraint + std::shared_ptr aConstraint = + PlaneGCSSolver_Tools::createMovementConstraint(theFeatureOrPoint); + if (aConstraint) { + SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked); + if (aConstraint->error().empty()) { + aConstraint->startPoint(theFrom); + theStorage->adjustParametrizationOfArcs(); + theSketchSolver->initialize(); + aConstraint->moveTo(theTo); + theStorage->setNeedToResolve(true); + } else + theStorage->notify(aConstraint->movedFeature()); + } - // Secondly, search attributes of the feature in the list of the Multi constraints and update them - updateMultiConstraints(myConstraints, theFeature); + return aConstraint; } -// ============================================================================ -// Function: addWorkplane -// Class: SketchSolver_Group -// Purpose: create workplane for the group -// ============================================================================ -bool SketchSolver_Group::addWorkplane(CompositeFeaturePtr theSketch) +bool SketchSolver_Group::moveFeature(FeaturePtr theFeature, + const std::shared_ptr& theFrom, + const std::shared_ptr& theTo) { - if (myWorkplaneID != EID_UNKNOWN || theSketch->getKind() != SketchPlugin_Sketch::ID()) - return false; // the workplane already exists or the function parameter is not Sketch - - mySketch = theSketch; - if (!updateWorkplane()) { - mySketch = CompositeFeaturePtr(); - return false; - } + SolverConstraintPtr aConstraint = + move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo); + setTemporary(aConstraint); return true; } -// ============================================================================ -// Function: updateWorkplane -// Class: SketchSolver_Group -// Purpose: update parameters of workplane -// ============================================================================ -bool SketchSolver_Group::updateWorkplane() +bool SketchSolver_Group::movePoint(AttributePtr theAttribute, + const std::shared_ptr& theFrom, + const std::shared_ptr& theTo) { - BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); - if (!myStorage) // Create storage if not exists - myStorage = aBuilder->createStorage(getId()); - - // 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 isUpdated; + SolverConstraintPtr aConstraint = + move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo); + setTemporary(aConstraint); + return true; } // ============================================================================ @@ -269,202 +219,182 @@ bool SketchSolver_Group::updateWorkplane() // ============================================================================ bool SketchSolver_Group::resolveConstraints() { - bool aResolved = false; - bool isGroupEmpty = isEmpty(); - if (myStorage->isNeedToResolve() && !isGroupEmpty) { - if (!mySketchSolver) - mySketchSolver = SketchSolver_Manager::instance()->builder()->createSolver(); + static const int MAX_STACK_SIZE = 5; + // check the "Multi" constraints do not drop sketch into infinite loop + if (myMultiConstraintUpdateStack > MAX_STACK_SIZE) { + myMultiConstraintUpdateStack = 0; + myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED; + // generate error message due to loop update of the sketch + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR()) + ->setValue(SketchSolver_Error::INFINITE_LOOP()); + sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints); + return false; + } - mySketchSolver->setGroup(myID); - mySketchSolver->calculateFailedConstraints(false); - myStorage->initializeSolver(mySketchSolver); - mySketchSolver->prepare(); + bool aResolved = false; + bool isGroupEmpty = isEmpty() && myStorage->isEmpty(); + if (myStorage->isNeedToResolve() && + (!isGroupEmpty || !myConflictingConstraints.empty() || + myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) { - SketchSolver_SolveStatus aResult = STATUS_OK; + PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK; try { - if (myStorage->hasDuplicatedConstraint()) - 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; - while (true) { - aResult = mySketchSolver->solve(); - if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET || isLastChance) - break; - // 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); - } + if (!isGroupEmpty) { + myStorage->adjustParametrizationOfArcs(); + aResult = mySketchSolver->solve(); } + if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED && + !myTempConstraints.empty()) { + mySketchSolver->undo(); + removeTemporaryConstraints(); + aResult = mySketchSolver->solve(); + } + // check degenerated geometry after constraints resolving + if (aResult == PlaneGCSSolver_Solver::STATUS_OK) + aResult = myStorage->checkDegeneratedGeometry(); } catch (...) { -// Events_Error::send(SketchSolver_Error::SOLVESPACE_CRASH(), this); - getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::SOLVESPACE_CRASH()); - if (myPrevResult == STATUS_OK || myPrevResult == STATUS_UNKNOWN) { + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR()) + ->setValue(SketchSolver_Error::SOLVESPACE_CRASH()); + if (myPrevResult == PlaneGCSSolver_Solver::STATUS_OK || + myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) { // the error message should be changed before sending the message sendMessage(EVENT_SOLVER_FAILED); - myPrevResult = STATUS_FAILED; + myPrevResult = PlaneGCSSolver_Solver::STATUS_FAILED; } mySketchSolver->undo(); return false; } - if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET) { // solution succeeded, store results into correspondent attributes + // solution succeeded, store results into correspondent attributes + if (aResult == PlaneGCSSolver_Solver::STATUS_OK || + aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) { + myStorage->setNeedToResolve(false); myStorage->refresh(); - if (myPrevResult != STATUS_OK || myPrevResult == STATUS_UNKNOWN) { - getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(""); - // the error message should be changed before sending the message - sendMessage(EVENT_SOLVER_REPAIRED); - myPrevResult = STATUS_OK; + + // additional check that copied entities used in Mirror and other "Multi" constraints + // is not connected with their originals by constraints. + myMultiConstraintUpdateStack += 1; + aResolved = true; + if (myStorage->isNeedToResolve()) + aResolved = resolveConstraints(); + + if (aResolved) { + myMultiConstraintUpdateStack -= 1; + + if (myPrevResult != PlaneGCSSolver_Solver::STATUS_OK || + myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN) { + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(""); + std::set aConflicting = myConflictingConstraints; + myConflictingConstraints.clear(); + myPrevResult = PlaneGCSSolver_Solver::STATUS_OK; + // the error message should be changed before sending the message + sendMessage(EVENT_SOLVER_REPAIRED, aConflicting); + } } + + // show degrees of freedom + computeDoF(); } else { mySketchSolver->undo(); if (!myConstraints.empty()) { -// Events_Error::send(SketchSolver_Error::CONSTRAINTS(), this); - getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(SketchSolver_Error::CONSTRAINTS()); - if (myPrevResult != aResult || myPrevResult == STATUS_UNKNOWN) { - // the error message should be changed before sending the message - sendMessage(EVENT_SOLVER_FAILED); + // the error message should be changed before sending the message + const std::string& aErrorMsg = aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED ? + SketchSolver_Error::DEGENERATED_GEOMETRY() : + SketchSolver_Error::CONSTRAINTS(); + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(aErrorMsg); + if (myPrevResult != aResult || + myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN || + myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) { + // Obtain list of conflicting constraints + std::set aConflicting = myStorage->getConflictingConstraints(mySketchSolver); + + if (!myConflictingConstraints.empty()) { + 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; + if (!myConflictingConstraints.empty()) + sendMessage(EVENT_SOLVER_FAILED, myConflictingConstraints); myPrevResult = aResult; } } + + // show degrees of freedom only if the degenerated geometry appears + if (aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED) + computeDoF(); } - 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(); + } + else if (isGroupEmpty && isWorkplaneValid()) { + // clear error related to previously degenerated entities + if (myPrevResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED) { + getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(""); + myPrevResult = PlaneGCSSolver_Solver::STATUS_OK; + // the error message should be changed before sending the message + myConflictingConstraints.clear(); + sendMessage(EVENT_SOLVER_REPAIRED, myConflictingConstraints); + } + + computeDoF(); } removeTemporaryConstraints(); - myStorage->blockEvents(false); myStorage->setNeedToResolve(false); return aResolved; } // ============================================================================ -// Function: mergeGroups +// Function: computeDoF // Class: SketchSolver_Group -// Purpose: append specified group to the current group +// Purpose: compute DoF of the sketch and set corresponding field // ============================================================================ -void SketchSolver_Group::mergeGroups(const SketchSolver_Group& theGroup) +void SketchSolver_Group::computeDoF() { - // If specified group is empty, no need to merge - if (theGroup.isEmpty()) - return; - - std::set aConstraints; - ConstraintConstraintMap::const_iterator aConstrIter = theGroup.myConstraints.begin(); - for (; aConstrIter != theGroup.myConstraints.end(); aConstrIter++) - 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); - } + std::string aDoFMsg; + static const std::string aMsgContext("Sketch"); + int aDoF = mySketchSolver->dof(); + /// "DoF = 0" content of string value is used in PartSet by Sketch edit + /// If it is changed, it should be corrected also there + //if (aDoF == 0) { + // static const std::string aMsgDoF("Sketch is fully fixed (DoF = 0)"); + // aDoFMsg = Config_Translator::translate(aMsgContext, aMsgDoF); + //} else { + // static const std::string aMsgDoF("DoF (degrees of freedom) = %1"); + // Events_InfoMessage aMsg(aMsgContext, aMsgDoF); + // aMsg.addParameter(aDoF); + // aDoFMsg = Config_Translator::translate(aMsg); + //} + //// store Unicode value for translated message about DoF + //size_t aLen = aDoFMsg.size(); + //std::wstring aWStr(aLen, L'#'); + //mbstowcs(&aWStr[0], aDoFMsg.c_str(), aLen); + //mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aWStr); + + std::ostringstream aStr; + aStr << aDoF; + mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aStr.str()); + + if (aDoF > 0 && myDOF <= 0) + sendMessage(EVENT_SKETCH_UNDER_CONSTRAINED, mySketch, aDoF); + else if (aDoF == 0 && myDOF != 0) + sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF); + else if (aDoF < 0) + sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF); + + myDOF = aDoF; } // ============================================================================ -// Function: splitGroup -// Class: SketchSolver_Group -// Purpose: divide the group into several subgroups -// ============================================================================ -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 - std::list anUnusedConstraints; - ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); - for ( ; aCIter != myConstraints.end(); aCIter++) { - 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::list::iterator aUnuseIt = anUnusedConstraints.begin(); - while (aUnuseIt != anUnusedConstraints.end()) { - if (aNewStorage->isInteract(FeaturePtr(*aUnuseIt))) { - aNewStorage->addConstraint(*aUnuseIt, aDummyVec); - anUnusedConstraints.erase(aUnuseIt); - aUnuseIt = anUnusedConstraints.begin(); - continue; - } - aUnuseIt++; - } - - std::list::iterator aCutsIter; - aUnuseIt = anUnusedConstraints.begin(); - 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) - if ((*aCutsIter)->isInteract(*aUnuseIt)) { - (*aCutsIter)->changeConstraint(*aUnuseIt); - break; - } - if (aCutsIter == theCuts.end()) { - // Add new group - 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); - } - } - } -} - -// ============================================================================ -// Function: isConsistent +// Function: repairConsistency // Class: SketchSolver_Group // Purpose: search removed entities and constraints // ============================================================================ -bool SketchSolver_Group::isConsistent() +void SketchSolver_Group::repairConsistency() { - if (isEmpty()) // no one constraint is initialized yet - return true; - - // 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) { + if (!areConstraintsValid() || !myStorage->areFeaturesValid()) { // remove invalid constraints std::set anInvalidConstraints; ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); @@ -475,10 +405,13 @@ bool SketchSolver_Group::isConsistent() std::set::const_iterator aRemoveIt = anInvalidConstraints.begin(); for (; aRemoveIt != anInvalidConstraints.end(); ++aRemoveIt) removeConstraint(*aRemoveIt); + // remove invalid features myStorage->removeInvalidEntities(); + + // show DoF + computeDoF(); } - return aResult; } // ============================================================================ @@ -489,14 +422,17 @@ bool SketchSolver_Group::isConsistent() // ============================================================================ void SketchSolver_Group::removeTemporaryConstraints() { - std::set::iterator aTmpIt = myTempConstraints.begin(); - for (; aTmpIt != myTempConstraints.end(); ++aTmpIt) - (*aTmpIt)->remove(); + if (!myTempConstraints.empty()) { + mySketchSolver->removeConstraint(CID_MOVEMENT); + + std::set::iterator aTmpIt = myTempConstraints.begin(); + for (; aTmpIt != myTempConstraints.end(); ++aTmpIt) + (*aTmpIt)->remove(); + + myTempConstraints.clear(); + } - if (!myTempConstraints.empty()) - myStorage->verifyFixed(); myStorage->setNeedToResolve(false); - myTempConstraints.clear(); } // ============================================================================ @@ -510,6 +446,9 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint) for (; aCIter != myConstraints.end(); aCIter++) if (aCIter->first == theConstraint) { aCIter->second->remove(); // the constraint is not fully removed + + // constraint is removed => reset stack of "multi" constraints updates + myMultiConstraintUpdateStack = 0; break; } if (aCIter != myConstraints.end()) @@ -523,103 +462,37 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint) // ============================================================================ void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint) { - myTempConstraints.insert(theConstraint); + if (theConstraint) + myTempConstraints.insert(theConstraint); } - // ============================================================================ -// Function: checkFeatureValidity +// Function: blockEvents // Class: SketchSolver_Group -// Purpose: verifies is the feature valid +// Purpose: block or unblock events from features in this group // ============================================================================ -bool SketchSolver_Group::checkFeatureValidity(FeaturePtr theFeature) +void SketchSolver_Group::blockEvents(bool isBlocked) { - if (!theFeature || !theFeature->data()->isValid()) - return true; - - SessionPtr aMgr = ModelAPI_Session::get(); - ModelAPI_ValidatorsFactory* aFactory = aMgr->validators(); - return aFactory->validate(theFeature); -} - + if (myIsEventsBlocked == isBlocked) + return; + // block/unblock events from the features in the storage + myStorage->blockEvents(isBlocked); + // block/unblock events from constraints + ConstraintConstraintMap::iterator aCIt = myConstraints.begin(); + for (; aCIt != myConstraints.end(); ++aCIt) + aCIt->second->blockEvents(isBlocked); -// =========== 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; + myIsEventsBlocked = isBlocked; } -static bool isLess(FeaturePtr theFeature1, FeaturePtr theFeature2) +bool SketchSolver_Group::areConstraintsValid() const { - 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; + // Check the constraints are valid + ConstraintConstraintMap::const_iterator aCIter = myConstraints.begin(); + for (; aCIter != myConstraints.end(); ++aCIter) + if (!aCIter->first->data() || !aCIter->first->data()->isValid()) + return false; + return true; } -