Salome HOME
cosmétique
[modules/shaper.git] / src / SketchSolver / SketchSolver_ConstraintTangent.cpp
index c54b01e91608855b7f2d3f855fa9e346ddec4c5e..6edf462c9710a0b8f352c092ea8f69787a63a6a3 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+// Copyright (C) 2014-2022  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
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_Ellipse2d.h>
 
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <ModelAPI_AttributeInteger.h>
+
 #include <SketchPlugin_Arc.h>
+#include <SketchPlugin_BSpline.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_ConstraintCoincidence.h>
+#include <SketchPlugin_ConstraintCoincidenceInternal.h>
 #include <SketchPlugin_ConstraintMiddle.h>
 
 #include <cmath>
@@ -111,14 +117,20 @@ void SketchSolver_ConstraintTangent::rebuild()
   mySharedPoint = AttributePtr();
   if (myAuxPoint) {
     GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
+    if (myAuxParameters[0])
+      aParams.insert(myAuxParameters[0]->scalar());
+    if (myAuxParameters[1])
+      aParams.insert(myAuxParameters[1]->scalar());
     aStorage->removeParameters(aParams);
     myAuxPoint = EntityWrapperPtr();
+    myAuxParameters[0] = myAuxParameters[1] = ScalarWrapperPtr();
   }
 
   // Check the quantity of entities of each type and their order (arcs first)
   int aNbLines = 0;
   int aNbCircles = 0;
   int aNbEllipses = 0;
+  int aNbSplines = 0;
   std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
   for (; anEntIt != myAttributes.end(); ++anEntIt) {
     if (!(*anEntIt).get())
@@ -129,16 +141,18 @@ void SketchSolver_ConstraintTangent::rebuild()
       ++aNbCircles;
     else if ((*anEntIt)->type() == ENTITY_ELLIPSE || (*anEntIt)->type() == ENTITY_ELLIPTIC_ARC)
       ++aNbEllipses;
+    else if ((*anEntIt)->type() == ENTITY_BSPLINE)
+      ++aNbSplines;
   }
 
-  if (aNbCircles + aNbEllipses < 1) {
+  if (aNbCircles + aNbEllipses + aNbSplines < 1) {
     myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
     return;
   }
   if (aNbLines == 1 && aNbCircles == 1) {
     myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
   }
-  else if (aNbLines + aNbCircles + aNbEllipses == 2) {
+  else if (aNbLines + aNbCircles + aNbEllipses + aNbSplines == 2) {
     myType = CONSTRAINT_TANGENT_CURVE_CURVE;
     isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
   }
@@ -147,20 +161,61 @@ void SketchSolver_ConstraintTangent::rebuild()
     return;
   }
 
-  FeaturePtr aFeature1, aFeature2;
-  getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
+  FeaturePtr aFeatures[2];
+  getTangentFeatures(myBaseConstraint, aFeatures[0], aFeatures[1]);
 
   // check number of coincident points
-  std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
+  std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeatures[0], aFeatures[1]);
   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) {
     myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
     return;
   }
 
-  // Try to find non-boundary points coincident with both features.
-  // It is necesasry to create tangency with ellipse
-  if (aCoincidentPoints.empty() && aNbEllipses > 0)
-    aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
+  EntityWrapperPtr aTgEntities[2] = { myAttributes.front(), myAttributes.back() };
+
+  if (aCoincidentPoints.empty()) {
+    // Try to find non-boundary points coincident with both features.
+    // It is necesasry to create tangency with ellipse.
+    if (aNbEllipses > 0)
+      aCoincidentPoints = coincidentPoints(aFeatures[0], aFeatures[1]);
+  }
+  else if (aNbSplines > 0) {
+    // General approach applying tangency to B-spline leads to hang-up in PlaneGCS.
+    // So, the tangency will be applied for the construction segment instead of B-spline curve.
+    for (int i = 0; i < 2; ++i) {
+      if (aTgEntities[i]->type() == ENTITY_BSPLINE) {
+        EdgeWrapperPtr anEdge =
+            std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aTgEntities[i]);
+        std::shared_ptr<GCS::BSpline> aBSpline =
+            std::dynamic_pointer_cast<GCS::BSpline>(anEdge->entity());
+
+        // which boundary is coincident?
+        GCS::Point aPoint1, aPoint2;
+        for (std::set<AttributePtr>::iterator aPIt = aCoincidentPoints.begin();
+             aPIt != aCoincidentPoints.end(); ++aPIt) {
+          if ((*aPIt)->owner() == aFeatures[i]) {
+            if ((*aPIt)->id() == SketchPlugin_BSpline::START_ID()) {
+              aPoint1 = aBSpline->poles[0];
+              aPoint2 = aBSpline->poles[1];
+            }
+            else if ((*aPIt)->id() == SketchPlugin_BSpline::END_ID()) {
+              aPoint1 = aBSpline->poles[aBSpline->poles.size() - 2];
+              aPoint2 = aBSpline->poles[aBSpline->poles.size() - 1];
+            }
+            break;
+          }
+        }
+
+        // substitute B-spline by its boundary segment
+        std::shared_ptr<GCS::Line> aSegment(new GCS::Line);
+        aSegment->p1 = aPoint1;
+        aSegment->p2 = aPoint2;
+        aTgEntities[i] = EdgeWrapperPtr(new PlaneGCSSolver_EdgeWrapper(aSegment));
+        --aNbSplines;
+        ++aNbLines;
+      }
+    }
+  }
 
   EntityWrapperPtr aSharedPointEntity;
   std::list<GCSConstraintPtr> anAuxConstraints;
@@ -168,32 +223,36 @@ void SketchSolver_ConstraintTangent::rebuild()
     mySharedPoint = *aCoincidentPoints.begin();
     aSharedPointEntity = myStorage->entity(mySharedPoint);
   }
-  else if (aNbEllipses > 0) {
+  else if (aNbEllipses + aNbSplines > 0) {
     // create auxiliary point
     GCSPointPtr aPoint(new GCS::Point);
     aPoint->x = aStorage->createParameter();
     aPoint->y = aStorage->createParameter();
-    calculateTangencyPoint(myAttributes.front(), myAttributes.back(), aPoint);
+    calculateTangencyPoint(aTgEntities[0], aTgEntities[1], aPoint);
 
     myAuxPoint.reset(new PlaneGCSSolver_PointWrapper(aPoint));
     aSharedPointEntity = myAuxPoint;
 
-    // create auxiliary coincident constraints for tangency with ellipse
     EntityWrapperPtr aDummy;
-    ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
-        CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.front(), aDummy);
-    anAuxConstraints = aCoincidence->constraints();
-    aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
-        CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.back(), aDummy);
-    anAuxConstraints.insert(anAuxConstraints.end(),
-        aCoincidence->constraints().begin(), aCoincidence->constraints().end());
+    for (int i = 0; i < 2; ++i) {
+      // create auxiliary parameters for coincidence with B-spline
+      if (aTgEntities[i]->type() == ENTITY_BSPLINE)
+        myAuxParameters[i].reset(new PlaneGCSSolver_ScalarWrapper(aStorage->createParameter()));
+
+      // create auxiliary coincident constraints for tangency with ellipse
+      ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
+          CONSTRAINT_PT_ON_CURVE, myAuxParameters[i],
+          aSharedPointEntity, aDummy, aTgEntities[i], aDummy);
+      anAuxConstraints.insert(anAuxConstraints.end(),
+          aCoincidence->constraints().begin(), aCoincidence->constraints().end());
+    }
   }
 
   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
-    mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
+    mySolverConstraint = createArcLineTangency(aTgEntities[0], aTgEntities[1],
                                            aSharedPointEntity, &myCurveCurveAngle);
   } else {
-    mySolverConstraint = createCurveCurveTangency(myAttributes.front(), myAttributes.back(),
+    mySolverConstraint = createCurveCurveTangency(aTgEntities[0], aTgEntities[1],
                             isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
   }
 
@@ -281,6 +340,24 @@ void SketchSolver_ConstraintTangent::notify(const FeaturePtr&      theFeature,
     rebuild();
 }
 
+bool SketchSolver_ConstraintTangent::remove()
+{
+  if (myAuxPoint) {
+    std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+        std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+    GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
+    if (myAuxParameters[0])
+      aParams.insert(myAuxParameters[0]->scalar());
+    if (myAuxParameters[1])
+      aParams.insert(myAuxParameters[1]->scalar());
+    aStorage->removeParameters(aParams);
+    myAuxPoint = EntityWrapperPtr();
+    myAuxParameters[0] = myAuxParameters[1] = ScalarWrapperPtr();
+  }
+  return SketchSolver_Constraint::remove();
+}
+
 
 
 
@@ -301,13 +378,22 @@ std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theF
   const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
 
   std::set<FeaturePtr> aCoincidences;
+  std::map<AttributePtr, FeaturePtr> aCoincidentPoints;
   std::set<AttributePtr>::const_iterator anIt;
 
   // collect coincidences referred to the first feature
   for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
-    if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
+    if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+                 aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID())) {
       aCoincidences.insert(aRef);
+      AttributeRefAttrPtr aRefAttrA = aRef->refattr(SketchPlugin_Constraint::ENTITY_A());
+      if (!aRefAttrA->isObject())
+        aCoincidentPoints[aRefAttrA->attr()] = aRef;
+      AttributeRefAttrPtr aRefAttrB = aRef->refattr(SketchPlugin_Constraint::ENTITY_B());
+      if (!aRefAttrB->isObject())
+        aCoincidentPoints[aRefAttrB->attr()] = aRef;
+    }
   }
 
   // leave only coincidences referred to the second feature
@@ -316,6 +402,20 @@ std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theF
     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
     if (aCoincidences.find(aRef) != aCoincidences.end())
       aCoincidencesBetweenFeatures.insert(aRef);
+    else if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+                      aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID())) {
+      for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+        AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+        if (aRefAttr && !aRefAttr->isObject()) {
+          std::map<AttributePtr, FeaturePtr>::iterator aFound =
+              aCoincidentPoints.find(aRefAttr->attr());
+          if (aFound != aCoincidentPoints.end()) {
+            aCoincidencesBetweenFeatures.insert(aRef);
+            aCoincidencesBetweenFeatures.insert(aFound->second);
+          }
+        }
+      }
+    }
   }
 
   return aCoincidencesBetweenFeatures;
@@ -325,26 +425,50 @@ std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeatureP
 {
   std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
   // collect points only
-  std::set<AttributePtr> aCoincidentPoints;
+  std::map<FeaturePtr, std::set<AttributePtr> > aCoincidentPoints;
   std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
   for (; aCIt != aCoincidences.end(); ++ aCIt) {
-    AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
-    AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
-    if (!aRefAttrA || aRefAttrA->isObject() ||
-        !aRefAttrB || aRefAttrB->isObject())
-      continue;
-
-    AttributePtr anAttrA = aRefAttrA->attr();
-    AttributePtr anAttrB = aRefAttrB->attr();
-    if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
-        anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
-        anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
-        anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
-      aCoincidentPoints.insert(anAttrA);
-      aCoincidentPoints.insert(anAttrB);
+    for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+      AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+      if (!aRefAttr || aRefAttr->isObject())
+        continue;
+
+      AttributePtr anAttr = aRefAttr->attr();
+      FeaturePtr anOwner = ModelAPI_Feature::feature(anAttr->owner());
+      if (anOwner == theFeature1 || anOwner == theFeature2) {
+        if (anAttr->id() == SketchPlugin_BSplineBase::POLES_ID()) {
+          AttributePoint2DArrayPtr aPoles =
+              std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(anAttr);
+
+          AttributeIntegerPtr anIndex;
+          if (anOwner->getKind() == SketchPlugin_BSpline::ID()) {
+            anIndex = (*aCIt)->integer(i == 0 ?
+                SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A() :
+                SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
+          }
+          if (anIndex) {
+            if (anIndex->value() == 0)
+              anAttr = anOwner->attribute(SketchPlugin_BSpline::START_ID());
+            else if (anIndex->value() + 1 == aPoles->size())
+              anAttr = anOwner->attribute(SketchPlugin_BSpline::END_ID());
+            if (anAttr)
+              aCoincidentPoints[anOwner].insert(anAttr);
+          }
+        }
+        else if (anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
+                 anAttr->id() != SketchPlugin_Circle::CENTER_ID())
+          aCoincidentPoints[anOwner].insert(anAttr);
+      }
     }
   }
-  return aCoincidentPoints;
+
+  std::set<AttributePtr> aBoundaryPoints;
+  if (aCoincidentPoints.size() == 2) {
+    for (std::map<FeaturePtr, std::set<AttributePtr> >::iterator anIt = aCoincidentPoints.begin();
+         anIt != aCoincidentPoints.end(); ++anIt)
+      aBoundaryPoints.insert(anIt->second.begin(), anIt->second.end());
+  }
+  return aBoundaryPoints;
 }
 
 static std::set<AttributePtr> refsToFeatureAndResults(FeaturePtr theFeature)
@@ -368,6 +492,7 @@ static std::set<AttributePtr> pointsOnFeature(FeaturePtr theFeature)
   for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
     if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+                 aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID() ||
                  aRef->getKind() == SketchPlugin_ConstraintMiddle::ID())) {
       for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
         AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));