Salome HOME
Fix incorrect processing of the copied entities after the "Multi" constraint has...
[modules/shaper.git] / src / SketchSolver / PlaneGCSSolver / PlaneGCSSolver_Solver.cpp
index d0f503aba13e3b6764d3a917a1b3e8d42978a8a4..57529dc35510836b69f62ac5f02261abaf3ffd44 100644 (file)
@@ -4,15 +4,16 @@
 // Created: 14 Dec 2014
 // Author:  Artem ZHIDKOV
 
-#include "PlaneGCSSolver_Solver.h"
+#include <PlaneGCSSolver_Solver.h>
 #include <Events_LongOp.h>
 
-#include <cmath>
-
 
 PlaneGCSSolver_Solver::PlaneGCSSolver_Solver()
   : myEquationSystem(new GCS::System),
-  myConfCollected(false)
+    myDiagnoseBeforeSolve(false),
+    myInitilized(false),
+    myConfCollected(false),
+    myDOF(0)
 {
 }
 
@@ -24,37 +25,84 @@ PlaneGCSSolver_Solver::~PlaneGCSSolver_Solver()
 void PlaneGCSSolver_Solver::clear()
 {
   myEquationSystem->clear();
-  myConstraints.clear();
   myParameters.clear();
+  myConstraints.clear();
+  myConflictingIDs.clear();
+  myDOF = 0;
 }
 
-void PlaneGCSSolver_Solver::addConstraint(GCSConstraintPtr theConstraint,
-    const SketchSolver_ConstraintType theType)
+void PlaneGCSSolver_Solver::addConstraint(GCSConstraintPtr theConstraint)
 {
-  GCS::Constraint* aConstraint = theConstraint.get();
-  if (myConstraints.find(aConstraint) != myConstraints.end())
-    return; // constraint already exists, no need to add it again
+  myEquationSystem->addConstraint(theConstraint.get());
+  myConstraints[theConstraint->getTag()].insert(theConstraint);
+  if (theConstraint->getTag() >= 0)
+    myDOF = -1;
+}
 
-  myEquationSystem->addConstraint(aConstraint);
-  myConstraints[aConstraint] = theType;
+void PlaneGCSSolver_Solver::removeConstraint(ConstraintID theID)
+{
+  myConstraints.erase(theID);
+  if (myConstraints.empty()) {
+    myEquationSystem->clear();
+    myDOF = (int)myParameters.size();
+  } else {
+    myEquationSystem->clearByTag(theID);
+    if (theID >= 0)
+      myDOF = -1;
+  }
 }
 
-void PlaneGCSSolver_Solver::removeConstraint(GCSConstraintPtr theConstraint)
+double* PlaneGCSSolver_Solver::createParameter()
 {
-  GCS::Constraint* aConstraint = theConstraint.get();
-  removeConstraint(aConstraint);
+  double* aResult = new double(0);
+  myParameters.push_back(aResult);
+  if (myConstraints.empty() && myDOF >= 0)
+    ++myDOF; // calculate DoF by hand if and only if there is no constraints yet
+  else
+    myDiagnoseBeforeSolve = true;
+  return aResult;
 }
 
-void PlaneGCSSolver_Solver::removeConstraint(GCS::Constraint* theConstraint)
+void PlaneGCSSolver_Solver::addParameters(const GCS::SET_pD& theParams)
 {
-  if (myConstraints.find(theConstraint) == myConstraints.end())
-    return; // no constraint, no need to remove it
+  GCS::SET_pD aParams(theParams);
+  // leave new parameters only
+  GCS::VEC_pD::iterator anIt = myParameters.begin();
+  for (; anIt != myParameters.end(); ++anIt)
+    if (aParams.find(*anIt) != aParams.end())
+      aParams.erase(*anIt);
+
+  myParameters.insert(myParameters.end(), aParams.begin(), aParams.end());
+  if (myConstraints.empty() && myDOF >=0)
+    myDOF += (int)aParams.size(); // calculate DoF by hand if and only if there is no constraints yet
+  else
+    myDiagnoseBeforeSolve = true;
+}
+
+void PlaneGCSSolver_Solver::removeParameters(const GCS::SET_pD& theParams)
+{
+  for (int i = (int)myParameters.size() - 1; i >= 0; --i)
+    if (theParams.find(myParameters[i]) != theParams.end()) {
+      myParameters.erase(myParameters.begin() + i);
+      --myDOF;
+    }
+  if (!myConstraints.empty())
+    myDiagnoseBeforeSolve = true;
+}
+
+void PlaneGCSSolver_Solver::initialize()
+{
+  Events_LongOp::start(this);
+  if (myDiagnoseBeforeSolve)
+    diagnose();
+  myEquationSystem->declareUnknowns(myParameters);
+  myEquationSystem->initSolution();
+  Events_LongOp::end(this);
 
-  myEquationSystem->removeConstraint(theConstraint);
-  myConstraints.erase(theConstraint);
+  myInitilized = true;
 }
 
-SketchSolver_SolveStatus PlaneGCSSolver_Solver::solve()
+PlaneGCSSolver_Solver::SolveStatus PlaneGCSSolver_Solver::solve()
 {
   // clear list of conflicting constraints
   if (myConfCollected) {
@@ -62,193 +110,39 @@ SketchSolver_SolveStatus PlaneGCSSolver_Solver::solve()
     myConfCollected = false;
   }
 
-  if (myConstraints.empty())
-    return STATUS_EMPTYSET;
   if (myParameters.empty())
     return STATUS_INCONSISTENT;
 
-  Events_LongOp::start(this);
   GCS::SolveStatus aResult = GCS::Success;
-  // if there is a constraint with all attributes constant, set fail status
-  GCS::SET_pD aParameters;
-  aParameters.insert(myParameters.begin(), myParameters.end());
-  ConstraintMap::const_iterator aConstrIt = myConstraints.begin();
-  for (; aConstrIt != myConstraints.end(); ++aConstrIt) {
-    GCS::VEC_pD aParams = aConstrIt->first->params();
-    GCS::VEC_pD::const_iterator aPIt = aParams.begin();
-    for (; aPIt != aParams.end(); ++aPIt)
-      if (aParameters.find(*aPIt) != aParameters.end())
-        break;
-    if (aPIt == aParams.end() && aConstrIt->first->getTag() > 0) {
-      myConflictingIDs.insert(aConstrIt->first->getTag());
-      myConfCollected = true;
-      aResult = GCS::Failed;
-    }
-  }
-  // solve equations
-  if (aResult == GCS::Success)
+  Events_LongOp::start(this);
+  if (myInitilized) {
+    aResult = (GCS::SolveStatus)myEquationSystem->solve();
+  } else {
+    if (myDiagnoseBeforeSolve)
+      diagnose();
     aResult = (GCS::SolveStatus)myEquationSystem->solve(myParameters);
-
-  GCS::VEC_I aRedundantID;
-
-  // Workaround: the system with tangent constraint may fail if the tangent entities are connected smoothly.
-  // Investigate this situation and move constraints to redundant list
-  if (aResult == GCS::Failed && !myTangent.empty()) {
-    GCS::VEC_I aConflictingID;
-    myEquationSystem->getConflicting(aConflictingID);
-    GCS::VEC_I::iterator aCIt = aConflictingID.begin();
-    for (; aCIt != aConflictingID.end(); ++ aCIt) {
-      if (myTangent.find(*aCIt) == myTangent.end())
-        continue;
-      if (isTangentTruth(*aCIt))
-        aRedundantID.push_back(*aCIt);
-    }
-
-    if (!aRedundantID.empty())
-      aResult = GCS::Success; // check redundant constraints
-  }
-
-  // Additionally check redundant constraints
-  if (aResult == GCS::Success || aResult == GCS::Converged) {
-    GCS::VEC_I aRedundantLocal;
-    myEquationSystem->getRedundant(aRedundantLocal);
-    aRedundantID.insert(aRedundantID.end(), aRedundantLocal.begin(), aRedundantLocal.end());
-    // Workaround: remove all point-point coincidences from list of redundant
-    if (!aRedundantID.empty()) {
-      ConstraintMap::const_iterator aCIt = myConstraints.begin();
-      for (; aCIt != myConstraints.end(); ++aCIt) {
-        if (aCIt->second != CONSTRAINT_PT_PT_COINCIDENT)
-          continue;
-        GCS::VEC_I::iterator aRIt = aRedundantID.begin();
-        for (; aRIt != aRedundantID.end(); ++aRIt)
-          if (aCIt->first->getTag() == *aRIt) {
-            aRedundantID.erase(aRIt);
-            break;
-          }
-      }
-    }
-    // The system with tangent constraints may show redundant constraints if the entities are coupled smoothly.
-    // Sometimes tangent constraints are fall to both conflicting and redundant constraints.
-    // Need to check if there are redundant constraints without these tangencies.
-    if (!aRedundantID.empty())
-      aResult = myTangent.empty() ? GCS::Failed : solveWithoutTangent();
-    else
-      aResult = GCS::Success;
   }
   Events_LongOp::end(this);
 
-  SketchSolver_SolveStatus aStatus;
-  if (aResult == GCS::Success) {
+  // collect information about conflicting constraints every time,
+  // sometimes solver reports about succeeded recalculation but has conflicting constraints
+  // (for example, apply horizontal constraint for a copied feature)
+  collectConflicting();
+  if (!myConflictingIDs.empty())
+    aResult = GCS::Failed;
+
+  SolveStatus aStatus;
+  if (aResult == GCS::Failed)
+    aStatus = STATUS_FAILED;
+  else {
     myEquationSystem->applySolution();
+    if (myDOF < 0)
+      myDOF = myEquationSystem->dofsNumber();
     aStatus = STATUS_OK;
-  } else
-    aStatus = STATUS_FAILED;
-
-  return aStatus;
-}
-
-GCS::SolveStatus PlaneGCSSolver_Solver::solveWithoutTangent()
-{
-  std::shared_ptr<GCS::System> aSystemWithoutTangent(new GCS::System);
-
-  // Remove tangency which leads to redundant or conflicting constraints
-  GCS::VEC_I aConflicting, aRedundant;
-  myEquationSystem->getRedundant(aRedundant);
-  size_t aNbRemove = myTangent.size(); // number of tangent constraints which can be removed
-  myEquationSystem->getConflicting(aConflicting);
-  aRedundant.insert(aRedundant.end(), aConflicting.begin(), aConflicting.end());
-
-  GCS::SET_I aTangentToRemove;
-  GCS::VEC_I::iterator aCIt = aRedundant.begin();
-  for (; aCIt != aRedundant.end() && aNbRemove > 0; ++aCIt)
-    if (myTangent.find(*aCIt) != myTangent.end()) {
-      aTangentToRemove.insert(*aCIt);
-      --aNbRemove;
-    }
-
-  std::set<GCS::Constraint*> aRemovedTangent;
-  ConstraintMap::const_iterator aConstrIt = myConstraints.begin();
-  while (aConstrIt != myConstraints.end()) {
-    GCS::Constraint* aConstraint = aConstrIt->first;
-    int anID = aConstraint->getTag();
-    ++aConstrIt;
-    if (aTangentToRemove.find(anID) == aTangentToRemove.end())
-      aSystemWithoutTangent->addConstraint(aConstraint);
-    else
-      aRemovedTangent.insert(aConstraint);
-  }
-
-  myTangent.clear();
-  GCS::SolveStatus aResult = (GCS::SolveStatus)aSystemWithoutTangent->solve(myParameters);
-  if (aResult == GCS::Success) {
-    GCS::VEC_I aRedundant;
-    aSystemWithoutTangent->getRedundant(aRedundant);
-    if (aRedundant.empty())
-      myEquationSystem = aSystemWithoutTangent;
-    else
-      aResult = GCS::Failed;
-  }
-
-  // additional check that removed constraints are still correct
-  if (aResult == GCS::Success) {
-    std::set<GCS::Constraint*>::const_iterator aRemIt = aRemovedTangent.begin();
-    for (; aRemIt != aRemovedTangent.end(); ++aRemIt)
-      if (!isTangentTruth(*aRemIt))
-        break;
-    if (aRemIt != aRemovedTangent.end())
-      aResult = GCS::Failed;
-  }
-
-  // Add IDs of removed tangent to the list of conflicting constraints
-  if (aResult == GCS::Failed) {
-    std::set<GCS::Constraint*>::const_iterator aRemIt = aRemovedTangent.begin();
-    for (; aRemIt != aRemovedTangent.end(); ++aRemIt)
-      myConflictingIDs.insert((*aRemIt)->getTag());
   }
 
-  return aResult;
-}
-
-bool PlaneGCSSolver_Solver::isTangentTruth(GCS::Constraint* theTangent) const
-{
-  static const double aTol = 1e-5;
-  double aTol2 = aTol *aTol;
-
-  if (theTangent->getTypeId() == GCS::TangentCircumf) {
-    GCS::VEC_pD aParams = theTangent->params();
-    double dx = *(aParams[2]) - *(aParams[0]);
-    double dy = *(aParams[3]) - *(aParams[1]);
-    double aDist2 = dx * dx + dy * dy;
-    double aRadSum  = *(aParams[4]) + *(aParams[5]);
-    double aRadDiff = *(aParams[4]) - *(aParams[5]);
-    aTol2 *= aDist2 > 1.0 ? aDist2 : 1.0;
-    return fabs(aDist2 - aRadSum * aRadSum) <= aTol2 ||
-           fabs(aDist2 - aRadDiff * aRadDiff) <= aTol2;
-  }
-  if (theTangent->getTypeId() == GCS::P2LDistance) {
-    GCS::VEC_pD aParams = theTangent->params();
-    double aDist2 = *(aParams[6]) * *(aParams[6]);
-    // orthogonal line direction
-    double aDirX = *(aParams[5]) - *(aParams[3]);
-    double aDirY = *(aParams[2]) - *(aParams[4]);
-    double aLen2 = aDirX * aDirX + aDirY * aDirY;
-    // vector from line's start to point
-    double aVecX = *(aParams[0]) - *(aParams[2]);
-    double aVecY = *(aParams[1]) - *(aParams[3]);
-
-    double aDot = aVecX * aDirX + aVecY * aDirY;
-    return fabs(aDot * aDot - aDist2 * aLen2) <= aTol2 * aLen2;
-  }
-  return false;
-}
-
-bool PlaneGCSSolver_Solver::isTangentTruth(int theTagID) const
-{
-  ConstraintMap::const_iterator anIt = myConstraints.begin();
-  for (; anIt != myConstraints.end(); ++anIt)
-    if (anIt->first->getTag() == theTagID)
-      return isTangentTruth(anIt->first);
-  return false;
+  myInitilized = false;
+  return aStatus;
 }
 
 void PlaneGCSSolver_Solver::undo()
@@ -275,7 +169,16 @@ void PlaneGCSSolver_Solver::collectConflicting()
   myConfCollected = true;
 }
 
-int PlaneGCSSolver_Solver::dof() const
+int PlaneGCSSolver_Solver::dof()
+{
+  if (myDOF < 0 && !myConstraints.empty())
+    diagnose();
+  return myDOF;
+}
+
+void PlaneGCSSolver_Solver::diagnose()
 {
-  return const_cast<PlaneGCSSolver_Solver*>(this)->myEquationSystem->dofsNumber();
+  myEquationSystem->declareUnknowns(myParameters);
+  myDOF = myEquationSystem->diagnose();
+  myDiagnoseBeforeSolve = false;
 }