Salome HOME
Issue #591 - Highlight of the first argument of constraints
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintMirror.cpp
index 8ed85ff49cbfc140c1e1b57065a9040ce602c8eb..6ce1ab65d69907dc56bb577202aeb03a2b565396 100644 (file)
@@ -10,6 +10,7 @@
 #include <GeomAPI_Dir2d.h>
 #include <GeomAPI_XY.h>
 
+#include <cmath>
 
 void SketchSolver_ConstraintMirror::getAttributes(
     Slvs_Entity& theMirrorLine,
@@ -57,7 +58,13 @@ void SketchSolver_ConstraintMirror::getAttributes(
         continue;
 
       anEntity = changeEntity(aFeature, aType);
-      aList->push_back(myStorage->getEntity(anEntity));
+      // Sort entities by their type
+      std::vector<Slvs_Entity>::iterator anIt = aList->begin();
+      for (; anIt != aList->end(); anIt++)
+        if (aType < anIt->type)
+          break;
+//      aList->push_back(myStorage->getEntity(anEntity));
+      aList->insert(anIt, myStorage->getEntity(anEntity));
     }
   }
 
@@ -186,15 +193,6 @@ void SketchSolver_ConstraintMirror::process()
       }
     }
   }
-
-  // Set the mirror line unchanged during constraint recalculation
-  for (int i = 0; i < 2; i++) {
-    aConstraint = Slvs_MakeConstraint(
-        SLVS_E_UNKNOWN, myGroup->getId(), SLVS_C_WHERE_DRAGGED, myGroup->getWorkplaneId(), 0.0,
-        aMirrorLine.point[i], SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, SLVS_E_UNKNOWN);
-    aConstraint.h = myStorage->addConstraint(aConstraint);
-    mySlvsConstraints.push_back(aConstraint.h);
-  }
 }
 
 
@@ -224,6 +222,10 @@ bool SketchSolver_ConstraintMirror::remove(ConstraintPtr theConstraint)
    isFullyRemoved = myStorage->removeConstraint(*aCIter) && isFullyRemoved;
   mySlvsConstraints.clear();
 
+  std::map<FeaturePtr, Slvs_hEntity>::iterator aFeatIt = myFeatureMap.begin();
+  for (; aFeatIt != myFeatureMap.end(); aFeatIt++)
+    myStorage->removeEntity(aFeatIt->second);
+
   if (isFullyRemoved) {
     myFeatureMap.clear();
     myAttributeMap.clear();
@@ -233,6 +235,39 @@ bool SketchSolver_ConstraintMirror::remove(ConstraintPtr theConstraint)
   return true;
 }
 
+bool SketchSolver_ConstraintMirror::checkAttributesChanged(ConstraintPtr theConstraint)
+{
+  // First of all, check the mirror line is changed.
+  // It may be changed to one of mirrored lines, which is already in this constraint
+  // (this case is not marked as attribute changing)
+  ConstraintPtr aConstraint = theConstraint ? theConstraint : myBaseConstraint;
+  AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
+      aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
+  if (!aRefAttr || !aRefAttr->isObject() || !aRefAttr->object())
+    return true;
+  FeaturePtr aMirrorLine = ModelAPI_Feature::feature(aRefAttr->object());
+  if (!aMirrorLine)
+    return true;
+
+  std::map<FeaturePtr, Slvs_hEntity>::iterator aMirrorIter = myFeatureMap.find(aMirrorLine);
+  if (aMirrorIter == myFeatureMap.end())
+    return true;
+
+  // Check the entity is not used as mirror line
+  std::vector<Slvs_hConstraint>::iterator aCIter = mySlvsConstraints.begin();
+  for (; aCIter != mySlvsConstraints.end(); aCIter++) {
+    Slvs_Constraint aMirrorConstr = myStorage->getConstraint(*aCIter);
+    if (aMirrorConstr.type != SLVS_C_SYMMETRIC_LINE)
+      continue;
+    if (aMirrorConstr.entityA != aMirrorIter->second)
+      return true;
+    else break; // check just one symmetric constraint
+  }
+
+  // Base verification
+  return SketchSolver_Constraint::checkAttributesChanged(theConstraint);
+}
+
 void SketchSolver_ConstraintMirror::makeMirrorEntity(
     const Slvs_Entity& theBase,
     const Slvs_Entity& theMirror,
@@ -248,6 +283,7 @@ void SketchSolver_ConstraintMirror::makeMirrorEntity(
     Slvs_hEntity aTmp = aMirrorPoint[2];
     aMirrorPoint[2] = aMirrorPoint[1];
     aMirrorPoint[1] = aTmp;
+    adjustArcPoints(theBase);
   }
   if (theBase.type == SLVS_E_POINT_IN_2D || theBase.type == SLVS_E_POINT_IN_3D) {
     aBasePoint[0] = theBase.h;
@@ -265,9 +301,14 @@ void SketchSolver_ConstraintMirror::makeMirrorEntity(
   // orthogonal direction
   aDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir->y(), -aDir->x()));
 
+  Slvs_hConstraint aFixed; // transient variable
   for (int i = 0; i < 4; i++) {
     if (aBasePoint[i] == SLVS_E_UNKNOWN || aMirrorPoint[i] == SLVS_E_UNKNOWN)
       continue;
+    // check the mirror point is not fixed
+    if (myStorage->isPointFixed(aMirrorPoint[i], aFixed, true))
+      continue;
+
     Slvs_Entity aPointEnt = myStorage->getEntity(aBasePoint[i]);
     double aBaseX = myStorage->getParameter(aPointEnt.param[0]).val;
     double aBaseY = myStorage->getParameter(aPointEnt.param[1]).val;
@@ -286,13 +327,41 @@ void SketchSolver_ConstraintMirror::makeMirrorEntity(
   }
 }
 
-void SketchSolver_ConstraintMirror::adjustConstraint()
+void SketchSolver_ConstraintMirror::adjustArcPoints(const Slvs_Entity& theArc) const
 {
-  // Search mirror between middle points on the arcs and recompute their coordinates
-  std::list<Slvs_Constraint> aPonCirc = myStorage->getConstraintsByType(SLVS_C_PT_ON_CIRCLE);
-  if (aPonCirc.empty())
-    return;
+  Slvs_Param aParam;
+  Slvs_Entity aPoint;
+  double anArcParams[3][2];
+  for (int i = 0; i < 3; i++) {
+    aPoint = myStorage->getEntity(theArc.point[i]);
+    for (int j = 0; j < 2; j++) {
+      aParam = myStorage->getParameter(aPoint.param[j]);
+      anArcParams[i][j] = aParam.val;
+      if (i > 0)
+        anArcParams[i][j] -= anArcParams[0][j];
+    }
+  }
+  double aRad2 = anArcParams[1][0] * anArcParams[1][0] + anArcParams[1][1] * anArcParams[1][1];
+  double aDist2 = anArcParams[2][0] * anArcParams[2][0] + anArcParams[2][1] * anArcParams[2][1];
+  if (std::fabs(aRad2 - aDist2) < tolerance)
+    return; // nothing to update (last point already on the arc)
+  if (aDist2 < tolerance)
+    return; // unable to update
+  double aCoeff = std::sqrt(aRad2 / aDist2);
+  anArcParams[2][0] *= aCoeff;
+  anArcParams[2][1] *= aCoeff;
+
+  // Update last point
+  aPoint = myStorage->getEntity(theArc.point[2]);
+  for (int i = 0; i < 2; i++) {
+    aParam = Slvs_MakeParam(aPoint.param[i], myGroup->getId(),
+        anArcParams[0][i] + anArcParams[2][i]);
+    myStorage->updateParameter(aParam);
+  }
+}
 
+void SketchSolver_ConstraintMirror::adjustConstraint()
+{
   AttributeRefAttrPtr aMirLineAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
       myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
   if (!aMirLineAttr || !aMirLineAttr->isInitialized() || !aMirLineAttr->isObject()) {
@@ -308,6 +377,7 @@ void SketchSolver_ConstraintMirror::adjustConstraint()
     return;
   Slvs_Entity aMirrorLine = myStorage->getEntity(aMirLineIter->second);
 
+  Slvs_Constraint aMirror;
   double aStartEnd[4];
   for (int i = 0; i < 2; i++) {
     Slvs_Entity aPoint = myStorage->getEntity(aMirrorLine.point[i]);
@@ -315,12 +385,17 @@ void SketchSolver_ConstraintMirror::adjustConstraint()
       aStartEnd[2*i+j] = myStorage->getParameter(aPoint.param[j]).val;
   }
 
-  Slvs_Constraint aMirror;
+  // Search mirror between middle points on the arcs and recompute their coordinates
+  std::map<Slvs_hEntity, Slvs_hEntity> aPointsOnCircles;
+  std::list<Slvs_Constraint> aMirrorPonCirc;
+  std::list<Slvs_Constraint> aPonCirc = myStorage->getConstraintsByType(SLVS_C_PT_ON_CIRCLE);
   std::vector<Slvs_hConstraint>::iterator aConstrIter = mySlvsConstraints.begin();
   for (; aConstrIter != mySlvsConstraints.end(); aConstrIter++) {
     aMirror = myStorage->getConstraint(*aConstrIter);
     if (aMirror.type != SLVS_C_SYMMETRIC_LINE)
       continue;
+    if (aMirror.entityA != aMirrorLine.h)
+      continue; // don't update another Mirror constraints
     Slvs_Constraint aPonCircA, aPonCircB;
     aPonCircA.h = SLVS_E_UNKNOWN;
     aPonCircB.h = SLVS_E_UNKNOWN;
@@ -333,24 +408,47 @@ void SketchSolver_ConstraintMirror::adjustConstraint()
     }
     if (aPonCircA.h == SLVS_E_UNKNOWN || aPonCircB.h == SLVS_E_UNKNOWN)
       continue;
+    aMirrorPonCirc.push_back(aMirror);
+    // Store point IDs to avoid their recalculation twice
+    aPointsOnCircles[aPonCircA.ptA] = aPonCircA.entityA;
+    aPointsOnCircles[aPonCircB.ptA] = aPonCircB.entityA;
+  }
+
+  // Recalculate positions of mirroring points
+  std::list<Slvs_Constraint> aMirrorList = myStorage->getConstraintsByType(SLVS_C_SYMMETRIC_LINE);
+  std::list<Slvs_Constraint>::iterator aMirIter = aMirrorList.begin();
+  for (; aMirIter != aMirrorList.end(); aMirIter++) {
+    if (aMirIter->entityA != aMirrorLine.h)
+      continue; // don't update another Mirror constraints
+    if (aPointsOnCircles.find(aMirIter->ptA) != aPointsOnCircles.end())
+      continue; // Avoid mirroring points on circles
+    Slvs_Entity aBase = myStorage->getEntity(aMirIter->ptA);
+    Slvs_Entity aMirror = myStorage->getEntity(aMirIter->ptB);
+    makeMirrorEntity(aBase, aMirror, aStartEnd);
+  }
 
-    bool aNeedToResolve = myStorage->isNeedToResolve();
+  bool aNeedToResolve = myStorage->isNeedToResolve();
+  for (aMirIter = aMirrorPonCirc.begin(); aMirIter != aMirrorPonCirc.end(); aMirIter++) {
+    // Make centers of arcs symmetric
+    Slvs_Entity aBaseArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptA]);
+    Slvs_Entity aBasePoint = myStorage->getEntity(aBaseArc.point[0]);
+    Slvs_Entity aMirrorArc = myStorage->getEntity(aPointsOnCircles[aMirIter->ptB]);
+    Slvs_Entity aMirrorPoint = myStorage->getEntity(aMirrorArc.point[0]);
+    makeMirrorEntity(aBasePoint, aMirrorPoint, aStartEnd);
     // Calculate middle point for base arc and mirrored point on mirror arc
-    Slvs_Entity aBaseArc = myStorage->getEntity(aPonCircA.entityA);
-    Slvs_Entity aBasePoint = myStorage->getEntity(aPonCircA.ptA);
+    aBasePoint = myStorage->getEntity(aMirIter->ptA);
     Slvs_Param aParamX = myStorage->getParameter(aBasePoint.param[0]);
     Slvs_Param aParamY = myStorage->getParameter(aBasePoint.param[1]);
     calculateMiddlePoint(aBaseArc, 0.5, aParamX.val, aParamY.val);
     myStorage->updateParameter(aParamX);
     myStorage->updateParameter(aParamY);
-    Slvs_Entity aMirrorArc = myStorage->getEntity(aPonCircB.entityA);
-    Slvs_Entity aMirrorPoint = myStorage->getEntity(aPonCircB.ptA);
+    aMirrorPoint = myStorage->getEntity(aMirIter->ptB);
     aParamX = myStorage->getParameter(aMirrorPoint.param[0]);
     aParamY = myStorage->getParameter(aMirrorPoint.param[1]);
     calculateMiddlePoint(aMirrorArc, 0.5, aParamX.val, aParamY.val);
     myStorage->updateParameter(aParamX);
     myStorage->updateParameter(aParamY);
-    // To avoid looped recalculations of sketch
-    myStorage->setNeedToResolve(aNeedToResolve);
   }
+  // Restore previous value to avoid looped recalculations of sketch
+  myStorage->setNeedToResolve(aNeedToResolve);
 }