Salome HOME
Correct misprint
[modules/shaper.git] / src / SketchSolver / SketchSolver_Group.cpp
index c5418065bc13ad9cb93e92aff9c14719cd3921ac..93bbfa69d406369bea25307cbe7a7ea0eb3d498e 100644 (file)
@@ -1,19 +1,38 @@
-// Copyright (C) 2014-20xx CEA/DEN, EDF R&D
-
-// File:    SketchSolver_Group.cpp
-// Created: 27 May 2014
-// Author:  Artem ZHIDKOV
+// Copyright (C) 2014-2017  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<mailto:webmaster.salome@opencascade.com>
+//
 
 #include "SketchSolver_Group.h"
 #include <SketchSolver_Error.h>
 #include <SketchSolver_Manager.h>
 
 #include <PlaneGCSSolver_Solver.h>
+#include <PlaneGCSSolver_Storage.h>
+#include <PlaneGCSSolver_Tools.h>
 
 #include <Events_InfoMessage.h>
 #include <ModelAPI_AttributeString.h>
 #include <ModelAPI_Events.h>
+#include <SketchPlugin_ConstraintMirror.h>
 #include <SketchPlugin_ConstraintRigid.h>
+#include <SketchPlugin_MultiRotation.h>
+#include <SketchPlugin_MultiTranslation.h>
 
 
 static void sendMessage(const char* theMessageName)
@@ -32,20 +51,37 @@ static void sendMessage(const char* theMessageName, const std::set<ObjectPtr>& t
   Events_Loop::loop()->send(aMessage);
 }
 
+static void sendMessage(const char* theMessageName,
+                        const CompositeFeaturePtr& theSketch,
+                        const int theDOF)
+{
+  std::shared_ptr<ModelAPI_SolverFailedMessage> aMessage =
+      std::shared_ptr<ModelAPI_SolverFailedMessage>(
+      new ModelAPI_SolverFailedMessage(Events_Loop::eventByName(theMessageName)));
+
+  std::set<ObjectPtr> 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(0),
+    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));
 }
 
 SketchSolver_Group::~SketchSolver_Group()
@@ -68,9 +104,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 +118,9 @@ bool SketchSolver_Group::changeConstraint(
   }
   else
     myConstraints[theConstraint]->update();
+
+  // constraint is created/updated => reset stack of "multi" constraints updates
+  myMultiConstraintUpdateStack = 0;
   return true;
 }
 
@@ -91,20 +129,56 @@ bool SketchSolver_Group::updateFeature(FeaturePtr theFeature)
   return myStorage->update(theFeature);
 }
 
-bool SketchSolver_Group::moveFeature(FeaturePtr theFeature)
+template <class Type>
+static SolverConstraintPtr move(StoragePtr theStorage,
+                                SolverPtr theSketchSolver,
+                                int theSketchDOF,
+                                bool theEventsBlocked,
+                                Type theFeatureOrPoint,
+                                const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
+                                const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
 {
-  BuilderPtr aBuilder = SketchSolver_Manager::instance()->builder();
+  bool isEntityExists = (theStorage->entity(theFeatureOrPoint).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<SketchSolver_ConstraintMovement> aConstraint =
+      PlaneGCSSolver_Tools::createMovementConstraint(theFeatureOrPoint);
+  if (aConstraint) {
+    SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
+    if (aConstraint->error().empty()) {
+      aConstraint->startPoint(theFrom);
+      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<GeomAPI_Pnt2d>& theFrom,
+                                     const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
+{
+  SolverConstraintPtr aConstraint =
+      move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo);
+  setTemporary(aConstraint);
+  return true;
+}
 
+bool SketchSolver_Group::movePoint(AttributePtr theAttribute,
+                                   const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
+                                   const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
+{
+  SolverConstraintPtr aConstraint =
+      move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo);
+  setTemporary(aConstraint);
   return true;
 }
 
@@ -115,66 +189,71 @@ 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();
+      (!isGroupEmpty || !myConflictingConstraints.empty() ||
+        myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED)) {
 
-////    mySketchSolver->calculateFailedConstraints(false);
-////    myStorage->initializeSolver();
-////    mySketchSolver->prepare();
-
-    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();
-        }
+      if (!isGroupEmpty)
+        aResult = mySketchSolver->solve();
+      if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
+          !myTempConstraints.empty()) {
+        mySketchSolver->undo();
+        removeTemporaryConstraints();
+        aResult = mySketchSolver->solve();
       }
     } 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<ObjectPtr> 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<ObjectPtr> 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
@@ -186,8 +265,8 @@ bool SketchSolver_Group::resolveConstraints()
         getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
           ->setValue(SketchSolver_Error::CONSTRAINTS());
         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<ObjectPtr> aConflicting = myStorage->getConflictingConstraints(mySketchSolver);
 
@@ -208,19 +287,7 @@ bool SketchSolver_Group::resolveConstraints()
       }
     }
 
-    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)
+  } else if (isGroupEmpty && isWorkplaneValid())
     computeDoF();
   removeTemporaryConstraints();
   myStorage->setNeedToResolve(false);
@@ -232,15 +299,26 @@ 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();
+  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)
     aDoFMsg << "Sketch is fully fixed (DoF = 0)";
   else
     aDoFMsg << "DoF (degrees of freedom) = " << aDoF;
   mySketch->string(SketchPlugin_Sketch::SOLVER_DOF())->setValue(aDoFMsg.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 +328,7 @@ void SketchSolver_Group::computeDoF() const
 // ============================================================================
 void SketchSolver_Group::repairConsistency()
 {
-  if (!myStorage->isConsistent()) {
+  if (!areConstraintsValid() || !myStorage->areFeaturesValid()) {
     // remove invalid constraints
     std::set<ConstraintPtr> anInvalidConstraints;
     ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
@@ -279,8 +357,7 @@ void SketchSolver_Group::repairConsistency()
 void SketchSolver_Group::removeTemporaryConstraints()
 {
   if (!myTempConstraints.empty()) {
-    std::dynamic_pointer_cast<PlaneGCSSolver_Solver>(
-        mySketchSolver)->removeConstraint(CID_MOVEMENT);
+    mySketchSolver->removeConstraint(CID_MOVEMENT);
 
     std::set<SolverConstraintPtr>::iterator aTmpIt = myTempConstraints.begin();
     for (; aTmpIt != myTempConstraints.end(); ++aTmpIt)
@@ -303,6 +380,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 +396,8 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
 // ============================================================================
 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
 {
-  myTempConstraints.insert(theConstraint);
+  if (theConstraint)
+    myTempConstraints.insert(theConstraint);
 }
 
 // ============================================================================
@@ -339,3 +420,13 @@ 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;
+}