Salome HOME
Task #3231: Sketcher Offset of a curve. Unit test.
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Offset.cpp
index 2d82f01539e95260d5122bfac8b831f58a55977a..f7f7b75e8b3ed805588ee61e35420f94e2de987e 100644 (file)
 #include <GeomAPI_Edge.h>
 #include <GeomAPI_Ellipse.h>
 #include <GeomAPI_ShapeExplorer.h>
+#include <GeomAPI_Wire.h>
+#include <GeomAPI_WireExplorer.h>
 
 #include <GeomDataAPI_Point2D.h>
 #include <GeomDataAPI_Point2DArray.h>
 
 #include <iostream>
 
+static const double tolerance = 1.e-7;
+
 SketchPlugin_Offset::SketchPlugin_Offset()
 {
 }
@@ -98,7 +102,6 @@ void SketchPlugin_Offset::execute()
   AttributeDoublePtr aValueAttr = real(VALUE_ID());
   if (!aValueAttr->isInitialized()) return;
   double aValue = aValueAttr->value();
-  const double tolerance = 1.e-7;
   if (aValue < tolerance) return;
 
   // 2.a. Reversed?
@@ -163,9 +166,24 @@ void SketchPlugin_Offset::execute()
       std::shared_ptr<GeomAlgoAPI_WireBuilder> aWireBuilder(
           new GeomAlgoAPI_WireBuilder(aTopoChain));
 
-      // 5.d. Make offset for each wire
+      GeomShapePtr aWireShape = aWireBuilder->shape();
+      GeomWirePtr aWire (new GeomAPI_Wire (aWireShape));
+
+      // Fix for a problem of offset side change with selection change.
+      // Wire direction is defined by the first selected edge of this wire.
+      if (!aWire->isClosed()) {
+        ListOfShape aModified;
+        // First selected edge of current chain
+        GeomShapePtr aFirstSel = aFeature->lastResult()->shape();
+        aWireBuilder->modified(aFirstSel, aModified);
+        GeomShapePtr aModFS = aModified.front();
+        if (aModFS->orientation() != aFirstSel->orientation())
+          aValue = -aValue;
+      }
+
+      // 5.d. Make offset for the wire
       std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape(
-          new GeomAlgoAPI_Offset(aPlane, aWireBuilder->shape(), aValue));
+          new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue));
 
       std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList(new GeomAlgoAPI_MakeShapeList);
       aMakeList->appendAlgo(aWireBuilder);
@@ -194,8 +212,6 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
   // 1. Find a single edge, coincident to theEndPoint by one of its ends
   if (!theEndPoint) return false;
 
-  std::shared_ptr<GeomAPI_Pnt2d> aP2d = theEndPoint->pnt();
-
   FeaturePtr aNextEdgeFeature;
   int nbFound = 0;
 
@@ -213,9 +229,11 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
   for (; aPointsIt != anAllCoincPoints.end(); aPointsIt++) {
     AttributePtr aP = (*aPointsIt);
     FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
+    bool isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
 
     // Condition 0: not auxiliary
-    if (aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) continue;
+    if (!isInSet && aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
+      continue;
 
     // Condition 1: not a point feature
     if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
@@ -223,11 +241,7 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
       if (aCoincFeature != theEdge) {
         // Condition 3: it is in the set of interest.
         //              Empty set means all sketch edges.
-        bool isInSet = true;
-        if (theEdgesSet.size()) {
-          isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
-        }
-        if (isInSet) {
+        if (isInSet || theEdgesSet.empty()) {
           // Condition 4: consider only features with two end points
           std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
           SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
@@ -297,6 +311,25 @@ static void setRefListValue(AttributeRefListPtr theList, int theListSize,
     theList->append(theValue);
 }
 
+// Reorder shapes according to the wire's order
+static void reorderShapes(ListOfShape& theShapes, GeomShapePtr theWire)
+{
+  std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aShapes;
+  aShapes.insert(theShapes.begin(), theShapes.end());
+  theShapes.clear();
+
+  GeomWirePtr aWire(new GeomAPI_Wire(theWire));
+  GeomAPI_WireExplorer anExp(aWire);
+  for (; anExp.more(); anExp.next()) {
+    GeomShapePtr aCurEdge = anExp.current();
+    auto aFound = aShapes.find(aCurEdge);
+    if (aFound != aShapes.end()) {
+      theShapes.push_back(aCurEdge);
+      aShapes.erase(aFound);
+    }
+  }
+}
+
 static void removeLastFromIndex(AttributeRefListPtr theList, int theListSize, int& theLastIndex)
 {
   if (theLastIndex < theListSize) {
@@ -351,8 +384,12 @@ void SketchPlugin_Offset::addToSketch(const ListOfMakeShape& theOffsetAlgos)
     GeomShapePtr aBaseShape = aBaseFeature->lastResult()->shape();
     ListOfShape aNewShapes;
     for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
-         anAlgoIt != theOffsetAlgos.end() && aNewShapes.empty(); ++anAlgoIt) {
+         anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
       (*anAlgoIt)->generated(aBaseShape, aNewShapes);
+      if (!aNewShapes.empty()) {
+        reorderShapes(aNewShapes, (*anAlgoIt)->shape());
+        break;
+      }
     }
 
     // store base feature
@@ -424,6 +461,14 @@ static void findOrCreateFeatureByKind(SketchPlugin_Sketch* theSketch,
                                       FeaturePtr& theFeature,
                                       std::list<ObjectPtr>& thePoolOfFeatures)
 {
+  if (theFeature) {
+    // check the feature type is the same as required
+    if (theFeature->getKind() != theFeatureKind) {
+      // return feature to the pool and try to find the most appropriate
+      thePoolOfFeatures.push_back(theFeature);
+      theFeature = FeaturePtr();
+    }
+  }
   if (!theFeature) {
     // try to find appropriate feature in the pool
     for (std::list<ObjectPtr>::iterator it = thePoolOfFeatures.begin();
@@ -473,13 +518,18 @@ void SketchPlugin_Offset::updateExistentOrCreateNew(const GeomShapePtr& theShape
 
     findOrCreateFeatureByKind(sketch(), SketchPlugin_Arc::ID(), theFeature, thePoolOfFeatures);
 
+    GeomDirPtr aCircNormal = aCircEdge->normal();
+    GeomDirPtr aSketchNormal = sketch()->coordinatePlane()->normal();
+    if (aSketchNormal->dot(aCircNormal) < -tolerance)
+      std::swap(aFP, aLP);
+
     bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
     std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-      (theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
+      (theFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
     std::dynamic_pointer_cast<GeomDataAPI_Point2D>
       (theFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
     std::dynamic_pointer_cast<GeomDataAPI_Point2D>
-      (theFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
+      (theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
     theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
   }
   else if (aResEdge->isCircle()) {
@@ -527,7 +577,8 @@ void SketchPlugin_Offset::updateExistentOrCreateNew(const GeomShapePtr& theShape
         (theFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
       std::dynamic_pointer_cast<GeomDataAPI_Point2D>
         (theFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
-      theFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipseEdge->minorRadius());
+      theFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(
+        anEllipseEdge->minorRadius());
     }
   }
   else {
@@ -554,17 +605,17 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
 {
   GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
   // Forced conversion to b-spline, if aCurve is not b-spline
-  GeomAPI_BSpline aBSpline (aCurve, /*isForced*/true);
+  GeomBSplinePtr aBSpline = GeomAPI_BSpline::convertToBSpline(aCurve);
 
-  const std::string& aBSplineKind = aBSpline.isPeriodic() ? SketchPlugin_BSplinePeriodic::ID()
-                                                          : SketchPlugin_BSpline::ID();
+  const std::string& aBSplineKind = aBSpline->isPeriodic() ? SketchPlugin_BSplinePeriodic::ID()
+                                                           : SketchPlugin_BSpline::ID();
   findOrCreateFeatureByKind(sketch(), aBSplineKind, theResult, thePoolOfFeatures);
 
-  theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
+  theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline->degree());
 
   AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
     (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
-  std::list<GeomPointPtr> aPoles = aBSpline.poles();
+  std::list<GeomPointPtr> aPoles = aBSpline->poles();
   aPolesAttr->setSize((int)aPoles.size());
   std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
   for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
@@ -574,7 +625,7 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
 
   AttributeDoubleArrayPtr aWeightsAttr =
       theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
-  std::list<double> aWeights = aBSpline.weights();
+  std::list<double> aWeights = aBSpline->weights();
   if (aWeights.empty()) { // rational B-spline
     int aSize = (int)aPoles.size();
     aWeightsAttr->setSize(aSize);
@@ -590,7 +641,7 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
 
   AttributeDoubleArrayPtr aKnotsAttr =
       theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
-  std::list<double> aKnots = aBSpline.knots();
+  std::list<double> aKnots = aBSpline->knots();
   int aSize = (int)aKnots.size();
   aKnotsAttr->setSize(aSize);
   std::list<double>::iterator aKIt = aKnots.begin();
@@ -599,7 +650,7 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
 
   AttributeIntArrayPtr aMultsAttr =
       theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
-  std::list<int> aMultiplicities = aBSpline.mults();
+  std::list<int> aMultiplicities = aBSpline->mults();
   aSize = (int)aMultiplicities.size();
   aMultsAttr->setSize(aSize);
   std::list<int>::iterator aMIt = aMultiplicities.begin();
@@ -647,6 +698,8 @@ bool SketchPlugin_Offset::findWires()
     }
   }
 
+  bool aWasBlocked = data()->blockSendAttributeUpdated(true);
+
   // Gather chains of edges
   for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
     FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
@@ -677,6 +730,7 @@ bool SketchPlugin_Offset::findWires()
   }
   // TODO: hilight selection in the viewer
 
+  data()->blockSendAttributeUpdated(aWasBlocked);
   return true;
 }