Salome HOME
Provide translation of DoF string
[modules/shaper.git] / src / SketchSolver / SketchSolver_Group.cpp
index fa98e284e79c2d4482bd83a15b4cf2e545e65647..ab9b38fc2df6789e841b275e22e425f2805d9e19 100644 (file)
@@ -1,8 +1,21 @@
-// 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 <SketchSolver_Error.h>
@@ -20,6 +33,8 @@
 #include <SketchPlugin_MultiRotation.h>
 #include <SketchPlugin_MultiTranslation.h>
 
+#include <Config_Translator.h>
+
 
 static void sendMessage(const char* theMessageName)
 {
@@ -56,18 +71,18 @@ static void sendMessage(const char* theMessageName,
 
 
 // ========================================================
-// =========  SketchSolver_Group  ===============
+// =========       SketchSolver_Group       ===============
 // ========================================================
 
 SketchSolver_Group::SketchSolver_Group(const CompositeFeaturePtr& theWorkplane)
-  : mySketch(theWorkplane),
-    myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
-    myDOF(0),
+  : myPrevResult(PlaneGCSSolver_Solver::STATUS_UNKNOWN),
+    myDOF(-1),
     myIsEventsBlocked(false),
     myMultiConstraintUpdateStack(0)
 {
   mySketchSolver = SolverPtr(new PlaneGCSSolver_Solver);
   myStorage = StoragePtr(new PlaneGCSSolver_Storage(mySketchSolver));
+  updateSketch(theWorkplane);
 }
 
 SketchSolver_Group::~SketchSolver_Group()
@@ -110,30 +125,90 @@ bool SketchSolver_Group::changeConstraint(
   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<GeomDataAPI_Point>(
+      theSketch->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
+  AttributeDirPtr aNorm = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+      theSketch->attribute(SketchPlugin_Sketch::NORM_ID()));
+  AttributeDirPtr aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+      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)
 {
   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)
 {
-  bool isFeatureExists = (myStorage->entity(theFeature).get() != 0);
-  if (myDOF == 0 && isFeatureExists) {
+  bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0);
+  if (theSketchDOF == 0 && isEntityExists) {
     // avoid moving elements of fully constrained sketch
-    myStorage->refresh();
-    return true;
+    theStorage->refresh();
+    return SolverConstraintPtr();
   }
 
   // Create temporary Fixed constraint
-  SolverConstraintPtr aConstraint = PlaneGCSSolver_Tools::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);
+      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<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;
 }
 
@@ -144,8 +219,10 @@ 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 > 1) {
+  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())
@@ -162,8 +239,19 @@ bool SketchSolver_Group::resolveConstraints()
 
     PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
     try {
-      if (!isGroupEmpty && myMultiConstraintUpdateStack <= 1)
+      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 (...) {
       getWorkplane()->string(SketchPlugin_Sketch::SOLVER_ERROR())
         ->setValue(SketchSolver_Error::SOLVESPACE_CRASH());
@@ -185,7 +273,6 @@ bool SketchSolver_Group::resolveConstraints()
       // additional check that copied entities used in Mirror and other "Multi" constraints
       // is not connected with their originals by constraints.
       myMultiConstraintUpdateStack += 1;
-      updateMultiConstraints();
       aResolved = true;
       if (myStorage->isNeedToResolve())
         aResolved = resolveConstraints();
@@ -210,8 +297,10 @@ 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 == PlaneGCSSolver_Solver::STATUS_UNKNOWN ||
             myPrevResult == PlaneGCSSolver_Solver::STATUS_FAILED) {
@@ -233,10 +322,25 @@ bool SketchSolver_Group::resolveConstraints()
           myPrevResult = aResult;
         }
       }
+
+      // show degrees of freedom only if the degenerated geometry appears
+      if (aResult == PlaneGCSSolver_Solver::STATUS_DEGENERATED)
+        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);
     }
 
-  } else if (isGroupEmpty && isWorkplaneValid())
     computeDoF();
+  }
   removeTemporaryConstraints();
   myStorage->setNeedToResolve(false);
   return aResolved;
@@ -249,17 +353,33 @@ bool SketchSolver_Group::resolveConstraints()
 // ============================================================================
 void SketchSolver_Group::computeDoF()
 {
-  std::ostringstream aDoFMsg;
+  std::string aDoFMsg;
+  static const std::string aMsgContext("Sketch");
   int aDoF = 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());
-
-  if (aDoF > 0 && myDOF == 0)
+  /// "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)
+  else if (aDoF == 0 && myDOF != 0)
     sendMessage(EVENT_SKETCH_FULLY_CONSTRAINED, mySketch, aDoF);
   else if (aDoF < 0)
     sendMessage(EVENT_SKETCH_OVER_CONSTRAINED, mySketch, aDoF);
@@ -274,7 +394,7 @@ void SketchSolver_Group::computeDoF()
 // ============================================================================
 void SketchSolver_Group::repairConsistency()
 {
-  if (!myStorage->isConsistent()) {
+  if (!areConstraintsValid() || !myStorage->areFeaturesValid()) {
     // remove invalid constraints
     std::set<ConstraintPtr> anInvalidConstraints;
     ConstraintConstraintMap::iterator aCIter = myConstraints.begin();
@@ -342,7 +462,8 @@ void SketchSolver_Group::removeConstraint(ConstraintPtr theConstraint)
 // ============================================================================
 void SketchSolver_Group::setTemporary(SolverConstraintPtr theConstraint)
 {
-  myTempConstraints.insert(theConstraint);
+  if (theConstraint)
+    myTempConstraints.insert(theConstraint);
 }
 
 // ============================================================================
@@ -366,18 +487,12 @@ void SketchSolver_Group::blockEvents(bool isBlocked)
   myIsEventsBlocked = isBlocked;
 }
 
-// ============================================================================
-//  Function: updateMultiConstraints
-//  Class:    SketchSolver_Group
-//  Purpose:  update multi constraints
-// ============================================================================
-void SketchSolver_Group::updateMultiConstraints()
+bool SketchSolver_Group::areConstraintsValid() const
 {
-  ConstraintConstraintMap::iterator anIt = myConstraints.begin();
-  for (; anIt != myConstraints.end(); ++anIt) {
-    if (anIt->first->getKind() == SketchPlugin_ConstraintMirror::ID() ||
-        anIt->first->getKind() == SketchPlugin_MultiRotation::ID() ||
-        anIt->first->getKind() == SketchPlugin_MultiTranslation::ID())
-      anIt->second->update();
-  }
+  // 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;
 }