X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2FSketchSolver%2FSketchSolver_Group.cpp;h=2b2df9856d5da54cad0fd6f12110bb9bf5b78244;hb=a74fbd0025bc36fc76b5559d960857f419446ecb;hp=c5418065bc13ad9cb93e92aff9c14719cd3921ac;hpb=a3d263fc39996448910ac7155de337e9868cfa1f;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_Group.cpp b/src/SketchSolver/SketchSolver_Group.cpp index c5418065b..2b2df9856 100644 --- a/src/SketchSolver/SketchSolver_Group.cpp +++ b/src/SketchSolver/SketchSolver_Group.cpp @@ -1,19 +1,39 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D - -// File: SketchSolver_Group.cpp -// Created: 27 May 2014 -// Author: Artem ZHIDKOV +// Copyright (C) 2014-2021 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 static void sendMessage(const char* theMessageName) @@ -32,20 +52,37 @@ static void sendMessage(const char* theMessageName, const std::set& t Events_Loop::loop()->send(aMessage); } +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); + + Events_Loop::loop()->send(aMessage); +} + // ======================================================== -// ========= SketchSolver_Group =============== +// ========= SketchSolver_Group =============== // ======================================================== SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane) - : mySketch(theWorkplane), - myPrevResult(STATUS_UNKNOWN), - myIsEventsBlocked(false) + : myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN), + myDOF(-1), + myIsEventsBlocked(false), + myMultiConstraintUpdateStack(0) { - BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); - mySketchSolver = aBuilder->createSolver(); - myStorage = aBuilder->createStorage(mySketchSolver); + mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver); + myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver)); + updateSketch(theWorkplane); } SketchSolver_Group::~SketchSolver_Group() @@ -68,9 +105,8 @@ bool SketchSolver_Group::changeConstraint( { bool isNewConstraint = myConstraints.find(theConstraint) == myConstraints.end(); if (isNewConstraint) { - BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); // Add constraint to the current group - SolverConstraintPtr aConstraint = aBuilder->createConstraint(theConstraint); + SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::createConstraint(theConstraint); if (!aConstraint) return false; aConstraint->process(myStorage, myIsEventsBlocked); @@ -83,6 +119,37 @@ bool SketchSolver_Group::changeConstraint( } else myConstraints[theConstraint]->update(); + + // constraint is created/updated => reset stack of "multi" constraints updates + myMultiConstraintUpdateStack = 0; + return true; +} + +bool SketchSolver_Group::updateSketch(CompositeFeaturePtr theSketch) +{ + 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; } @@ -91,20 +158,69 @@ bool SketchSolver_Group::updateFeature(FeaturePtr theFeature) return myStorage->update(theFeature); } -bool SketchSolver_Group::moveFeature(FeaturePtr theFeature) +template +static SolverConstraintPtr move(StoragePtr theStorage, + SolverPtr theSketchSolver, + int theSketchDOF, + bool theEventsBlocked, + Type theFeatureOrPoint, + const EntityWrapperPtr& theSolverEntity, + const std::shared_ptr& theFrom, + const std::shared_ptr& theTo) { - BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder(); + bool isEntityExists = (theSolverEntity.get() != 0); + if (theSketchDOF == 0 && isEntityExists) { + // avoid moving elements of fully constrained sketch + theStorage->refresh(); + return SolverConstraintPtr(); + } // Create temporary Fixed constraint - SolverConstraintPtr aConstraint = aBuilder->createMovementConstraint(theFeature); - if (!aConstraint) - return false; - aConstraint->process(myStorage, myIsEventsBlocked); - if (aConstraint->error().empty()) - setTemporary(aConstraint); - else - myStorage->notify(theFeature); + 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()); + } + return aConstraint; +} + +bool SketchSolver_Group::moveFeature(FeaturePtr theFeature, + const std::shared_ptr& theFrom, + const std::shared_ptr& theTo) +{ + EntityWrapperPtr anEntity = myStorage->entity(theFeature); + SolverConstraintPtr aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, + theFeature, anEntity, theFrom, theTo); + setTemporary(aConstraint); + return true; +} + +bool SketchSolver_Group::movePoint(AttributePtr theAttribute, + const int thePointIndex, + const std::shared_ptr& theFrom, + const std::shared_ptr& theTo) +{ + EntityWrapperPtr anEntity = myStorage->entity(theAttribute); + SolverConstraintPtr aConstraint; + if (thePointIndex < 0) { + aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, + theAttribute, anEntity, theFrom, theTo); + } + else { + aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, + std::pair(theAttribute, thePointIndex), anEntity, + theFrom, theTo); + } + setTemporary(aConstraint); return true; } @@ -115,66 +231,76 @@ bool SketchSolver_Group::moveFeature(FeaturePtr theFeature) // ============================================================================ bool SketchSolver_Group::resolveConstraints() { + 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; + } + bool aResolved = false; bool isGroupEmpty = isEmpty() && myStorage->isEmpty(); if (myStorage->isNeedToResolve() && - (!isGroupEmpty || !myConflictingConstraints.empty() || myPrevResult == STATUS_FAILED)) { -//// if (!mySketchSolver) -//// mySketchSolver = SketchSolver_Manager::instance()->builder()->createSolver(); - -//// mySketchSolver->calculateFailedConstraints(false); -//// myStorage->initializeSolver(); -//// mySketchSolver->prepare(); + (!isGroupEmpty || !myConflictingConstraints.empty() || + myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) { - SketchSolver_SolveStatus aResult = STATUS_OK; + PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK; try { if (!isGroupEmpty) { - // 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(); - } + 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) { + 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; } // solution succeeded, store results into correspondent attributes - if (aResult == STATUS_OK || aResult == STATUS_EMPTYSET) { + if (aResult == PlaneGCSSolver_Solver::STATUS_OK || + aResult == PlaneGCSSolver_Solver::STATUS_EMPTYSET) { myStorage->setNeedToResolve(false); myStorage->refresh(); -//// updateMultiConstraints(myConstraints); -//// // multi-constraints updated some parameters, need to store them -//// if (myStorage->isNeedToResolve()) -//// resolveConstraints(); - - if (myPrevResult != STATUS_OK || myPrevResult == STATUS_UNKNOWN) { - getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())->setValue(""); - std::set aConflicting = myConflictingConstraints; - myConflictingConstraints.clear(); - myPrevResult = STATUS_OK; - // the error message should be changed before sending the message - sendMessage(EVENT_SOLVER_REPAIRED, aConflicting); + + // 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 @@ -183,11 +309,13 @@ bool SketchSolver_Group::resolveConstraints() 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()); + 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 == STATUS_UNKNOWN || - myPrevResult == STATUS_FAILED) { + myPrevResult == PlaneGCSSolver_Solver::STATUS_UNKNOWN || + myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) { // Obtain list of conflicting constraints std::set aConflicting = myStorage->getConflictingConstraints(mySketchSolver); @@ -206,22 +334,26 @@ bool SketchSolver_Group::resolveConstraints() myPrevResult = aResult; } } + + // show degrees of freedom only if the degenerated geometry appears, + // or if DoF is not computed yet + if (aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED || myDOF < 0) + computeDoF(); + } + + } + 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); } - aResolved = true; - } else if (!isGroupEmpty) { - // Check if the group contains only constraints Fixed, update parameters by stored values - aResolved = true; - ConstraintConstraintMap::iterator aCIt = myConstraints.begin(); - for (; aCIt != myConstraints.end(); ++aCIt) - if (aCIt->first->getKind() != SketchPlugin_ConstraintRigid::ID()) { - aResolved = false; - break; - } - if (aCIt == myConstraints.end()) - myStorage->refresh(); - } else if (isGroupEmpty) computeDoF(); + } removeTemporaryConstraints(); myStorage->setNeedToResolve(false); return aResolved; @@ -232,15 +364,40 @@ bool SketchSolver_Group::resolveConstraints() // Class: SketchSolver_Group // Purpose: compute DoF of the sketch and set corresponding field // ============================================================================ -void SketchSolver_Group::computeDoF() const +void SketchSolver_Group::computeDoF() { - std::ostringstream aDoFMsg; - int aDoF = /*isEmpty() ? myStorage->numberOfParameters() :*/ mySketchSolver->dof(); - if (aDoF == 0) - aDoFMsg << "Sketch is fully fixed (DoF = 0)"; - else - aDoFMsg << "DoF (degrees of freedom) = " << aDoF; - mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.str()); + 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; } // ============================================================================ @@ -250,7 +407,7 @@ void SketchSolver_Group::computeDoF() const // ============================================================================ void SketchSolver_Group::repairConsistency() { - if (!myStorage->isConsistent()) { + if (!areConstraintsValid() || !myStorage->areFeaturesValid()) { // remove invalid constraints std::set anInvalidConstraints; ConstraintConstraintMap::iterator aCIter = myConstraints.begin(); @@ -279,8 +436,7 @@ void SketchSolver_Group::repairConsistency() void SketchSolver_Group::removeTemporaryConstraints() { if (!myTempConstraints.empty()) { - std::dynamic_pointer_cast( - mySketchSolver)->removeConstraint(CID_MOVEMENT); + mySketchSolver->removeConstraint(CID_MOVEMENT); std::set::iterator aTmpIt = myTempConstraints.begin(); for (; aTmpIt != myTempConstraints.end(); ++aTmpIt) @@ -303,6 +459,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()) @@ -316,7 +475,8 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint) // ============================================================================ void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint) { - myTempConstraints.insert(theConstraint); + if (theConstraint) + myTempConstraints.insert(theConstraint); } // ============================================================================ @@ -339,3 +499,18 @@ void SketchSolver_Group::blockEvents(bool isBlocked) myIsEventsBlocked = isBlocked; } + +bool SketchSolver_Group::areConstraintsValid() const +{ + // 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; +} + +void SketchSolver_Group::underconstrainedFeatures(std::set& theFeatures) const +{ + myStorage->getUnderconstrainedGeometry(theFeatures); +}