]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Merge remote-tracking branch 'remotes/origin/master' into CEA_2019
authorazv <azv@opencascade.com>
Fri, 6 Sep 2019 11:20:21 +0000 (14:20 +0300)
committerazv <azv@opencascade.com>
Fri, 6 Sep 2019 11:20:21 +0000 (14:20 +0300)
# Conflicts:
# src/PartSet/PartSet_SketcherMgr.cpp

59 files changed:
src/ConstructionPlugin/ConstructionPlugin_Plugin.cpp
src/GeomAPI/GeomAPI_AISObject.cpp
src/GeomAPI/GeomAPI_Dir2d.h
src/GeomAPI/GeomAPI_Lin2d.h
src/GeomAlgoAPI/GeomAlgoAPI_Circ2dBuilder.cpp
src/GeomAlgoAPI/GeomAlgoAPI_Circ2dBuilder.h
src/ModuleBase/ModuleBase_Tools.cpp
src/ModuleBase/ModuleBase_WidgetMultiSelector.cpp
src/PartSet/PartSet_Module.cpp
src/PartSet/PartSet_OperationPrs.cpp
src/PartSet/PartSet_SketcherMgr.cpp
src/PartSet/PartSet_SketcherMgr.h
src/PartSet/PartSet_SketcherReentrantMgr.cpp
src/PartSet/PartSet_SketcherReentrantMgr.h
src/PartSet/PartSet_WidgetSketchLabel.cpp
src/PartSet/PartSet_WidgetSketchLabel.h
src/SketchAPI/SketchAPI_MacroArc.cpp
src/SketchAPI/SketchAPI_MacroArc.h
src/SketchAPI/SketchAPI_Sketch.cpp
src/SketchAPI/SketchAPI_Sketch.h
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_MacroArc.cpp
src/SketchPlugin/SketchPlugin_MacroArc.h
src/SketchPlugin/SketchPlugin_Plugin.cpp
src/SketchPlugin/SketchPlugin_Validators.cpp
src/SketchPlugin/SketchPlugin_Validators.h
src/SketchPlugin/Test/TestConstraintMiddlePointOnArc.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintPerpendicularArcLine.py [new file with mode: 0644]
src/SketchPlugin/Test/TestCreateArcByTransversalLine.py [new file with mode: 0644]
src/SketchPlugin/doc/arcFeature.rst
src/SketchPlugin/doc/examples/arc.py
src/SketchPlugin/doc/images/Arc_panel_3pt.png
src/SketchPlugin/doc/images/Arc_panel_base.png
src/SketchPlugin/doc/images/Arc_panel_perp.png [new file with mode: 0644]
src/SketchPlugin/doc/images/Arc_panel_tang.png
src/SketchPlugin/doc/images/Arc_res.png
src/SketchPlugin/doc/images/Perpendicular_panel.png
src/SketchPlugin/doc/images/arc_perp_32x32.png [new file with mode: 0644]
src/SketchPlugin/doc/perpendicularFeature.rst
src/SketchPlugin/icons/arc_perp_32x32.png [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/PlaneGCSSolver/CMakeLists.txt
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.cpp [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.h [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_FeatureBuilder.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/SketchSolver_ConstraintMiddle.cpp
src/SketchSolver/SketchSolver_ConstraintMiddle.h
src/SketchSolver/SketchSolver_Group.cpp
src/SketchSolver/SketchSolver_Storage.h
src/SketcherPrs/SketcherPrs_Perpendicular.cpp
src/XGUI/XGUI_ContextMenuMgr.cpp
src/XGUI/XGUI_PropertyPanel.cpp
src/XGUI/XGUI_Workshop.cpp

index 0b0dc4bb297c7c602e1d9093af5f0e8fde747a91..637e37b4e8de2f9edff675305ab4198034e474f2 100644 (file)
@@ -58,6 +58,8 @@ ConstructionPlugin_Plugin::ConstructionPlugin_Plugin()
                                    PLANE_SIZE, "0", "1000");
   Config_PropManager::registerProp(SKETCH_TAB_NAME, "planes_thickness", "Thickness",
     Config_Prop::IntSpin, SKETCH_WIDTH);
+  Config_PropManager::registerProp(SKETCH_TAB_NAME, "angular_tolerance", "Angular tolerance",
+    Config_Prop::DblSpin, "0.04");
   Config_PropManager::registerProp(SKETCH_TAB_NAME, "rotate_to_plane",
     "Rotate to plane when selected", Config_Prop::Boolean, "false");
 
index 7a86b1e8c410e3e88eb24f06bc2c76febad1d672..45f57fa5a241a7a8b3012b8d0fe7c5f8a8d65b2e 100644 (file)
@@ -406,6 +406,10 @@ bool GeomAPI_AISObject::setDeflection(const double theDeflection)
       if (fabs(aCoefficient-theDeflection) > Precision::Confusion()) {
         isModified = true;
         anAISShape->SetOwnDeviationCoefficient(theDeflection);
+        Handle(Prs3d_Drawer) aDrawer = anAISShape->DynamicHilightAttributes();
+        if (!aDrawer.IsNull()) {
+          aDrawer->SetDeviationCoefficient(theDeflection);
+        }
         // redisplay is necessary here to update presentation in all modes
         // Standard True flag. Displayer uses Standard False flag. If it will be changed in
         // displayer, redisplay here will not be necessary. But performance should be checked.
index c525e8c3fc2a86e691d352b38d155c553db87e8b..1b7a0d35d0416ff7f1dda68865735a03d6c6bbe0 100644 (file)
@@ -66,5 +66,8 @@ class GeomAPI_Dir2d : public GeomAPI_Interface
   double angle(const std::shared_ptr<GeomAPI_Dir2d>& theArg) const;
 };
 
+//! Pointer on the object
+typedef std::shared_ptr<GeomAPI_Dir2d> GeomDir2dPtr;
+
 #endif
 
index 3fdda3048163340b611f9800183732be0c186845..725c28f1f09103db7cc48c6059e32de340cf6e3f 100644 (file)
@@ -76,5 +76,9 @@ class GeomAPI_Lin2d : public GeomAPI_Interface
   std::shared_ptr<GeomAPI_Pnt2d> shiftedLocation(double theShift) const;
 };
 
+
+//! Pointer on the object
+typedef std::shared_ptr<GeomAPI_Lin2d> GeomLine2dPtr;
+
 #endif
 
index 232a0d690d894f03d646a36bc8212a881b5b2931..5b20ddb137911d80a46a9c34f108658772d063fc 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <BRep_Tool.hxx>
 #include <ElCLib.hxx>
+#include <GccAna_Circ2d2TanOn.hxx>
 #include <GccAna_Circ2d2TanRad.hxx>
 #include <GccAna_Circ2d3Tan.hxx>
 #include <GccAna_Circ2dTanCen.hxx>
@@ -48,6 +49,7 @@ typedef std::vector< std::shared_ptr<GccEnt_QualifiedLin> >  VectorOfGccLine;
 // Provide different mechanisms to create circle:
 // * by passing points
 // * by tangent edges
+// * by transversal line
 // * with specified radius
 // * etc.
 class CircleBuilder
@@ -83,6 +85,19 @@ public:
     }
   }
 
+  void setTransversalLine(const std::shared_ptr<GeomAPI_Shape>& theLine)
+  {
+    if (!theLine)
+      return;
+
+    const TopoDS_Edge& anEdge = TopoDS::Edge(theLine->impl<TopoDS_Shape>());
+
+    double aFirst, aLast;
+    TopLoc_Location aLoc;
+    Handle(Geom2d_Curve) aCurve = BRep_Tool::CurveOnSurface(anEdge, myPlane, aLoc, aFirst, aLast);
+    myTransversalLine = CurveAdaptorPtr(new Geom2dAdaptor_Curve(aCurve, aFirst, aLast));
+  }
+
   void setPassingPoints(const std::vector< std::shared_ptr<GeomAPI_Pnt2d> >& thePoints)
   {
     std::vector< std::shared_ptr<GeomAPI_Pnt2d> >::const_iterator aPIt;
@@ -115,9 +130,13 @@ public:
       case 1:
         aResult = circleByPointAndTwoTangentCurves();
         break;
-      case 2:
-        aResult = circleByTwoPointsAndTangentCurve();
+      case 2: {
+        if (myTransversalLine)
+          aResult = circleByTwoPointsAndTransversalLine();
+        else
+          aResult = circleByTwoPointsAndTangentCurve();
         break;
+      }
       case 3:
         aResult = circleByThreePassingPoints();
         break;
@@ -342,6 +361,22 @@ private:
   }
 
 
+  Circ2dPtr circleByTwoPointsAndTransversalLine()
+  {
+    const gp_Pnt2d& aPoint1 = myPassingPoints[0];
+    const gp_Pnt2d& aPoint2 = myPassingPoints[1];
+
+    if (myTransversalLine && myTransversalLine->GetType() == GeomAbs_Line) {
+      GccAna_Circ2d2TanOn aCircleBuilder(aPoint1, aPoint2, myTransversalLine->Line(),
+                                         Precision::Confusion());
+      if (aCircleBuilder.NbSolutions() > 0)
+        return Circ2dPtr(new gp_Circ2d(aCircleBuilder.ThisSolution(1)));
+    }
+
+    return Circ2dPtr();
+  }
+
+
   Circ2dPtr circleByRadiusAndTwoTangentCurves()
   {
     VectorOfGccCirc aTgCirc;
@@ -467,6 +502,7 @@ private:
   std::shared_ptr<GeomAPI_Pnt2d> myCenter;
   std::vector<gp_Pnt2d> myPassingPoints;
   std::vector<CurveAdaptorPtr> myTangentShapes;
+  CurveAdaptorPtr myTransversalLine;
   double myRadius;
   std::shared_ptr<GeomAPI_Pnt2d> myClosestPoint;
 };
@@ -497,6 +533,12 @@ void GeomAlgoAPI_Circ2dBuilder::addTangentCurve(const std::shared_ptr<GeomAPI_Sh
     myTangentShapes.push_back(theEdge);
 }
 
+void GeomAlgoAPI_Circ2dBuilder::setTransversalLine(const std::shared_ptr<GeomAPI_Shape>& theEdge)
+{
+  if (theEdge->isEdge())
+    myTransversalLine = theEdge;
+}
+
 void GeomAlgoAPI_Circ2dBuilder::addPassingPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
 {
   myPassingPoints.push_back(thePoint);
@@ -526,6 +568,7 @@ std::shared_ptr<GeomAPI_Circ2d> GeomAlgoAPI_Circ2dBuilder::circle()
   CircleBuilder aCircleBuilder(myPlane);
   aCircleBuilder.setCenter(myCenter);
   aCircleBuilder.setTangentCurves(myTangentShapes);
+  aCircleBuilder.setTransversalLine(myTransversalLine);
   aCircleBuilder.setPassingPoints(myPassingPoints);
   aCircleBuilder.setClosestPoint(myClosestPoint);
   aCircleBuilder.setRadius(myRadius);
index c193e877dc8314128d221bfc8d15b219bb021ca6..3105b504795cef29c875f33dd4b1652dec91157a 100644 (file)
@@ -58,6 +58,10 @@ public:
   GEOMALGOAPI_EXPORT
   void addTangentCurve(const std::shared_ptr<GeomAPI_Shape>& theEdge);
 
+  /// \brief Constrain circle to be orthogonal to the given edge
+  GEOMALGOAPI_EXPORT
+  void setTransversalLine(const std::shared_ptr<GeomAPI_Shape>& theEdge);
+
   /// \brief Constrain circle to pass through the given point
   GEOMALGOAPI_EXPORT
   void addPassingPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
@@ -82,6 +86,7 @@ private:
   std::shared_ptr<GeomAPI_Pnt2d>                myCenter;
   std::vector< std::shared_ptr<GeomAPI_Pnt2d> > myPassingPoints;
   std::vector< std::shared_ptr<GeomAPI_Shape> > myTangentShapes;
+  std::shared_ptr<GeomAPI_Shape>                myTransversalLine;
   std::shared_ptr<GeomAPI_Pnt2d>                myClosestPoint;
   double                                        myRadius;
 };
index a3c4d0ebdbf3c7c874d2abc25315c6594763d22d..83d25c2ad8093b04cf335a8a458b206c748a9b7d 100644 (file)
@@ -156,7 +156,7 @@ void setFocus(QWidget* theWidget, const QString& theInfo)
 {
   theWidget->setFocus();
   // rectangle of focus is not visible on tool button widgets
-  theWidget->repaint();
+  theWidget->update();
 #ifdef DEBUG_SET_FOCUS
   qDebug(QString("setFocus: %1").arg(theInfo).toStdString().c_str());
 #endif
index 1740c62ffd732e623fd73c1bc67091195791889b..f338f8eaa8c6c7f8f33e09e911cf354f55b09893 100644 (file)
@@ -809,7 +809,7 @@ void ModuleBase_WidgetMultiSelector::updateSelectionList()
   }
 
   // We have to call repaint because sometimes the List control is not updated
-  myListView->getControl()->repaint();
+  myListView->getControl()->update();
 }
 
 //********************************************************************
index 7859a701c54d767aa784f40617a6538a50141bf4..3a8d9643e1594961cdac490f48336f7166d6ce09 100644 (file)
@@ -857,6 +857,8 @@ ModuleBase_ModelWidget* PartSet_Module::createWidgetByType(const std::string& th
     connect(aLabelWgt, SIGNAL(showConstraintToggled(int, bool)),
       mySketchMgr, SLOT(onShowConstraintsToggle(int, bool)));
     connect(aLabelWgt, SIGNAL(showFreePoints(bool)), mySketchMgr, SLOT(onShowPoints(bool)));
+    connect(aLabelWgt, SIGNAL(autoConstraints(bool)),
+      sketchReentranceMgr(), SLOT(onAutoConstraints(bool)));
     aLabelWgt->setShowPointsState(mySketchMgr->isShowFreePointsShown());
     aWgt = aLabelWgt;
   } else if (theType == "sketch-2dpoint_selector") {
@@ -1453,7 +1455,7 @@ void PartSet_Module::processEvent(const std::shared_ptr<Events_Message>& theMess
     aDisplayer->updateViewer();
     // Update tree items if they are expanded
     if (needUpdate) {
-      aTreeView->viewport()->repaint(aTreeView->viewport()->rect());
+      aTreeView->viewport()->update(aTreeView->viewport()->rect());
     }
   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
index f9d91312c53ca0bcbceb0afb742d854fe38dff0d..f27f35f1910f58dcb960a425dba5781c233621f7 100644 (file)
@@ -127,6 +127,9 @@ void PartSet_OperationPrs::Compute(
     // change deviation coefficient to provide more precise circle
     // as there is no result, the shape is processed to correct deviation. To be unified
     ModuleBase_Tools::setDefaultDeviationCoefficient(aShape, aDrawer);
+    Handle(Prs3d_Drawer) aHighlightDrawer = DynamicHilightAttributes();
+    if (!aHighlightDrawer.IsNull())
+      ModuleBase_Tools::setDefaultDeviationCoefficient(aShape, aHighlightDrawer);
 
     if (myUseAISWidth) {
       Handle(AIS_InteractiveObject) anIO = anIter.Value();
index e936d729bdd6952ba32ada7763f5cb52f6bf48df..1c4c03e07ac37eda0a63e73411c862524ba05dbe 100644 (file)
@@ -168,7 +168,7 @@ PartSet_SketcherMgr::PartSet_SketcherMgr(PartSet_Module* theModule)
   : QObject(theModule), myModule(theModule), myIsEditLaunching(false), myIsDragging(false),
     myDragDone(false), myIsMouseOverWindow(false),
     myIsMouseOverViewProcessed(true), myPreviousUpdateViewerEnabled(true),
-    myIsPopupMenuActive(false), myExternalPointsMgr(0)
+    myIsPopupMenuActive(false), myExternalPointsMgr(0), myNoDragMoving(false)
 {
   ModuleBase_IWorkshop* anIWorkshop = myModule->workshop();
   ModuleBase_IViewer* aViewer = anIWorkshop->viewer();
@@ -353,15 +353,23 @@ void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseE
 
   ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
   ModuleBase_IViewer* aViewer = aWorkshop->viewer();
-  if (!aViewer->canDragByMouse())
-    return;
+  //if (!aViewer->canDragByMouse())
+  //  return;
 
   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
                                                                (getCurrentOperation());
   if (!aFOperation)
     return;
 
-  if (aFOperation->isEditOperation()) {
+  bool isEditing = aFOperation->isEditOperation();
+  bool aCanDrag = aViewer->canDragByMouse();
+
+  //if (!aViewer->canDragByMouse() && isEditing) {
+  //  // Do not edit by dragging
+  //  return;
+  //}
+
+  if (isEditing) {
     // If the current widget is a selector, do nothing, it processes the mouse press
     ModuleBase_ModelWidget* anActiveWidget = getActiveWidget();
     if(anActiveWidget && anActiveWidget->isViewerSelector()) {
@@ -381,16 +389,10 @@ void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseE
     if ((!isSketchOpe) && (!isSketcher))
       return;
 
-    bool isEditing = aFOperation->isEditOperation();
-
     // Ignore creation sketch operation
     if ((!isSketcher) && (!isEditing))
       return;
 
-    Handle(AIS_InteractiveContext) aContext = aViewer->AISContext();
-    // Remember highlighted objects for editing
-    ModuleBase_ISelection* aSelect = aWorkshop->selection();
-
     bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
     storeSelection(aHasShift ? ST_SelectAndHighlightType : ST_HighlightType, myCurrentSelection);
 
@@ -406,9 +408,10 @@ void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseE
 
     get2dPoint(theWnd, theEvent, myCurrentPoint);
     if (isSketcher) {
-      myIsDragging = true;
-      myDragDone = false;
-
+      if (aCanDrag) {
+        myIsDragging = true;
+        myDragDone = false;
+      }
       myPreviousDrawModeEnabled = aViewer->enableDrawMode(false);
       launchEditing();
       if (aFeature.get() != NULL) {
@@ -434,9 +437,10 @@ void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseE
       myIsEditLaunching = !myModule->sketchReentranceMgr()->isInternalEditActive();
       aFOperation->commit();
 
-      myIsDragging = true;
-      myDragDone = false;
-
+      if (aCanDrag) {
+        myIsDragging = true;
+        myDragDone = false;
+      }
       myPreviousDrawModeEnabled = aViewer->enableDrawMode(false);
       launchEditing();
       myIsEditLaunching = aPrevLaunchingState;
@@ -466,27 +470,44 @@ void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouse
   bool aWasDragging = myIsDragging;
   myIsDragging = false;
 
-  if (myModule->sketchReentranceMgr()->processMouseReleased(theWnd, theEvent))
+  if (myModule->sketchReentranceMgr()->processMouseReleased(theWnd, theEvent)) {
     return;
-
+  }
   // if mouse is pressed when it was over view and at release the mouse is out of view, do nothing
-  if (!myIsMouseOverViewProcessed)
+  if (!myIsMouseOverViewProcessed) {
     return;
-
+  }
   ModuleBase_IViewer* aViewer = aWorkshop->viewer();
-  if (!aViewer->canDragByMouse())
-    return;
-  ModuleBase_Operation* aOp = getCurrentOperation();
+  //if (!aViewer->canDragByMouse())
+  //  return;
+  ModuleBase_OperationFeature* aOp =
+      dynamic_cast<ModuleBase_OperationFeature*>(getCurrentOperation());
   if (aOp) {
-    if (isNestedSketchOperation(aOp)) {
-      // Only for sketcher operations
-      if (aWasDragging) {
-        if (myDragDone) {
-          /// the previous selection is lost by mouse release in the viewer(Select method), but
-          /// it is still stored in myCurrentSelection. So, it is possible to restore selection
-          /// It is important for drag(edit with mouse) of sketch entities.
-          restoreSelection(myCurrentSelection);
-          myCurrentSelection.clear();
+    bool aStartNoDragOperation = !aViewer->canDragByMouse() && aOp->isEditOperation();
+    if (aStartNoDragOperation || myNoDragMoving) {
+      // Process edit operation without dragging
+      if (myCurrentSelection.size() > 0)
+        myNoDragMoving = !myNoDragMoving;
+      else
+        myNoDragMoving = false;
+      if (myNoDragMoving)
+        return;
+      else {
+        restoreSelection(myCurrentSelection);
+        myCurrentSelection.clear();
+      }
+    }
+    else {
+      if (isNestedSketchOperation(aOp)) {
+        // Only for sketcher operations
+        if (aWasDragging) {
+          if (myDragDone) {
+            /// the previous selection is lost by mouse release in the viewer(Select method), but
+            /// it is still stored in myCurrentSelection. So, it is possible to restore selection
+            /// It is important for drag(edit with mouse) of sketch entities.
+            restoreSelection(myCurrentSelection);
+            myCurrentSelection.clear();
+          }
         }
       }
     }
@@ -514,7 +535,6 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
     qDebug(QString("%1").arg(anInfo.size()).arg(anInfoStr).toStdString().c_str());
   }
 #endif
-
   if (myModule->sketchReentranceMgr()->processMouseMoved(theWnd, theEvent))
     return;
 
@@ -536,7 +556,7 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
       // the feature is to be erased here, but it is correct to call canDisplayObject because
       // there can be additional check (e.g. editor widget in distance constraint)
       ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
-                                                 (getCurrentOperation());
+        (getCurrentOperation());
       if (aFOperation) {
         FeaturePtr aFeature = aFOperation->feature();
         visualizeFeature(aFeature, aFOperation->isEditOperation(), canDisplayObject(aFeature));
@@ -548,28 +568,32 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
   }
   //myClickedPoint.clear();
 
-  if (myIsDragging) {
+  if (myIsDragging || myNoDragMoving) {
     // 1. the current selection is saved in the mouse press method in order to restore it after
     //    moving
     // 2. the enable selection in the viewer should be temporary switched off in order to ignore
     // mouse press signal in the viewer(it call Select for AIS context and the dragged objects are
     // deselected). This flag should be restored in the slot, processed the mouse release signal.
-
     ModuleBase_Operation* aCurrentOperation = getCurrentOperation();
     if (!aCurrentOperation)
       return;
     if (isSketchOperation(aCurrentOperation))
       return; // No edit operation activated
 
+#ifdef DRAGGING_DEBUG
+    QTime t;
+    t.start();
+#endif
+
     Handle(V3d_View) aView = theWnd->v3dView();
     gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), aView);
     Point aMousePnt;
     get2dPoint(theWnd, theEvent, aMousePnt);
 
     std::shared_ptr<GeomAPI_Pnt2d> anOriginalPosition = std::shared_ptr<GeomAPI_Pnt2d>(
-                            new GeomAPI_Pnt2d(myCurrentPoint.myCurX, myCurrentPoint.myCurY));
+      new GeomAPI_Pnt2d(myCurrentPoint.myCurX, myCurrentPoint.myCurY));
     std::shared_ptr<GeomAPI_Pnt2d> aCurrentPosition = std::shared_ptr<GeomAPI_Pnt2d>(
-                            new GeomAPI_Pnt2d(aMousePnt.myCurX, aMousePnt.myCurY));
+      new GeomAPI_Pnt2d(aMousePnt.myCurX, aMousePnt.myCurY));
 
     ModuleBase_IWorkshop* aWorkshop = myModule->workshop();
     XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(aWorkshop);
@@ -582,7 +606,7 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
     static Events_ID aMoveEvent = Events_Loop::eventByName(EVENT_OBJECT_MOVED);
     //static Events_ID aUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
     FeatureToSelectionMap::const_iterator anIt = myCurrentSelection.begin(),
-                                          aLast = myCurrentSelection.end();
+      aLast = myCurrentSelection.end();
     // 4. the features and attributes modification(move)
     bool isModified = false;
     for (; anIt != aLast; anIt++) {
@@ -592,7 +616,7 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
       // Process selection by attribute: the priority to the attribute
       if (!anAttributes.empty()) {
         std::set<AttributePtr>::const_iterator anAttIt = anAttributes.begin(),
-                                               anAttLast = anAttributes.end();
+          anAttLast = anAttributes.end();
         for (; anAttIt != anAttLast; anAttIt++) {
           AttributePtr anAttr = *anAttIt;
           if (anAttr.get() == NULL)
@@ -606,7 +630,7 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
               bool isImmutable = aPoint->setImmutable(true);
 
               std::shared_ptr<ModelAPI_ObjectMovedMessage> aMessage = std::shared_ptr
-                       <ModelAPI_ObjectMovedMessage>(new ModelAPI_ObjectMovedMessage(this));
+                <ModelAPI_ObjectMovedMessage>(new ModelAPI_ObjectMovedMessage(this));
               aMessage->setMovedAttribute(aPoint);
               aMessage->setOriginalPosition(anOriginalPosition);
               aMessage->setCurrentPosition(aCurrentPosition);
@@ -617,13 +641,14 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
             }
           }
         }
-      } else {
+      }
+      else {
         // Process selection by feature
         std::shared_ptr<SketchPlugin_Feature> aSketchFeature =
           std::dynamic_pointer_cast<SketchPlugin_Feature>(aFeature);
         if (aSketchFeature) {
           std::shared_ptr<ModelAPI_ObjectMovedMessage> aMessage = std::shared_ptr
-                    <ModelAPI_ObjectMovedMessage>(new ModelAPI_ObjectMovedMessage(this));
+            <ModelAPI_ObjectMovedMessage>(new ModelAPI_ObjectMovedMessage(this));
           aMessage->setMovedObject(aFeature);
           aMessage->setOriginalPosition(anOriginalPosition);
           aMessage->setCurrentPosition(aCurrentPosition);
@@ -646,6 +671,10 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
     aDisplayer->enableUpdateViewer(isEnableUpdateViewer);
     aDisplayer->updateViewer();
 
+#ifdef DRAGGING_DEBUG
+    cout << "Mouse move processing " << t.elapsed() << endl;
+#endif
+
     myDragDone = true;
     myCurrentPoint = aMousePnt;
   }
index c036615b5ee29855a4495cceedd625903b47b521..6dfe03cc25f850ba5aa564678d4b4aa82d6e008c 100644 (file)
@@ -344,6 +344,8 @@ public slots:
   /// Process sketch plane selected event
   void onPlaneSelected(const std::shared_ptr<GeomAPI_Pln>& thePln);
 
+  /// The slot is called when user checks "Show free points" button
+  /// \param toShow a state of the check box
   void onShowPoints(bool toShow);
 
 private slots:
@@ -461,6 +463,8 @@ private:
   PartSet_ExternalPointsMgr* myExternalPointsMgr;
 
   QMap<ResultPtr, Handle(AIS_Shape)> myPointsHighlight;
+
+  bool myNoDragMoving;
 };
 
 
index 60134597f37250120035825e71ac8e5ecc405b64..5ae47888989d7855d7ec04174d6044d4c5fa3794 100644 (file)
@@ -31,6 +31,9 @@
 
 #include "GeomDataAPI_Point2D.h"
 
+#include "GeomAPI_Lin2d.h"
+#include "GeomAPI_Dir2d.h"
+
 #include <ModuleBase_IPropertyPanel.h>
 #include <ModuleBase_ISelectionActivate.h>
 #include <ModuleBase_OperationFeature.h>
@@ -51,6 +54,8 @@
 #include <SketchPlugin_Point.h>
 #include <SketchPlugin_Trim.h>
 #include <SketchPlugin_Split.h>
+#include <SketchPlugin_ConstraintHorizontal.h>
+#include <SketchPlugin_ConstraintVertical.h>
 
 #include <XGUI_Workshop.h>
 #include <XGUI_ModuleConnector.h>
@@ -69,7 +74,8 @@ PartSet_SketcherReentrantMgr::PartSet_SketcherReentrantMgr(ModuleBase_IWorkshop*
   myRestartingMode(RM_None),
   myIsFlagsBlocked(false),
   myIsInternalEditOperation(false),
-  myNoMoreWidgetsAttribute("")
+  myNoMoreWidgetsAttribute(""),
+  myIsAutoConstraints(true)
 {
 }
 
@@ -171,6 +177,8 @@ bool PartSet_SketcherReentrantMgr::processMouseMoved(ModuleBase_IViewWindow* the
         workshop()->selector()->clearSelection();
 
         myPreviousFeature = aFOperation->feature();
+        if (myIsAutoConstraints)
+          addConstraints(myPreviousFeature);
         restartOperation();
         myPreviousFeature = FeaturePtr();
 
@@ -823,3 +831,47 @@ void PartSet_SketcherReentrantMgr::setInternalActiveWidget(ModuleBase_ModelWidge
       aPropertyPanel->setInternalActiveWidget(theWidget);
   }
 }
+
+void PartSet_SketcherReentrantMgr::onAutoConstraints(bool isOn)
+{
+  myIsAutoConstraints = isOn;
+}
+
+void PartSet_SketcherReentrantMgr::addConstraints(const FeaturePtr& theFeature)
+{
+  static GeomDir2dPtr myHorDir(new GeomAPI_Dir2d(1, 0));
+  static GeomDir2dPtr myVertDir(new GeomAPI_Dir2d(0, 1));
+
+  if (theFeature->getKind() == SketchPlugin_Line::ID()) {
+    std::shared_ptr<ModelAPI_Data> aData = theFeature->data();
+    std::shared_ptr<GeomDataAPI_Point2D> aPoint1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        aData->attribute(SketchPlugin_Line::START_ID()));
+    std::shared_ptr<GeomDataAPI_Point2D> aPoint2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        aData->attribute(SketchPlugin_Line::END_ID()));
+    if (aPoint1.get() && aPoint2.get()) {
+      GeomLine2dPtr aLine(new GeomAPI_Lin2d(aPoint1->pnt(), aPoint2->pnt()));
+      GeomDir2dPtr aDir = aLine->direction();
+      double aHorAngle = fabs(myHorDir->angle(aDir));
+      double aVertAngle = fabs(myVertDir->angle(aDir));
+      if (aHorAngle > M_PI/2.)
+        aHorAngle = M_PI - aHorAngle;
+      if (aVertAngle > M_PI/2.)
+        aVertAngle = M_PI - aVertAngle;
+
+      double aTolerance = Config_PropManager::real(SKETCH_TAB_NAME, "angular_tolerance");
+      CompositeFeaturePtr aSketch = module()->sketchMgr()->activeSketch();
+      FeaturePtr aFeature;
+      if (aHorAngle < aTolerance)
+        // Add horizontal constraint
+        aFeature = aSketch->addFeature(SketchPlugin_ConstraintHorizontal::ID());
+      else if (aVertAngle < aTolerance)
+        // Add vertical constraint
+        aFeature = aSketch->addFeature(SketchPlugin_ConstraintVertical::ID());
+
+      if (aFeature.get()) {
+        aFeature->refattr(SketchPlugin_Constraint::ENTITY_A())->setObject(
+            theFeature->firstResult());
+      }
+    }
+  }
+}
index fc2f6723cc9440e9edfa4e1b5cc1943b86d9ebbb..91ab34ebcef3c26f8cdefe4dca02fb8db0facd8c 100644 (file)
@@ -131,6 +131,15 @@ public:
   /// \param theMessage a message of reentrant operation
   void setReentrantPreSelection(const std::shared_ptr<Events_Message>& theMessage);
 
+  bool isAutoConstraints() const { return myIsAutoConstraints; }
+
+
+public slots:
+  /// The slot is called when user checks "Automatic constraints" button
+  /// \param isOn a state of the check box
+  void onAutoConstraints(bool isOn);
+
+
 private slots:
   /// SLOT, that is called by a widget activating in the property panel
   /// If the 'internal' edit operation is started, it activates the first widget selection
@@ -211,6 +220,8 @@ private:
 
   void setInternalActiveWidget(ModuleBase_ModelWidget* theWidget);
 
+  void addConstraints(const FeaturePtr& theFeature);
+
 private:
   ModuleBase_IWorkshop* myWorkshop; /// the workshop
 
@@ -227,6 +238,8 @@ private:
   ObjectPtr mySelectedObject; /// cashed selected object
   std::shared_ptr<ModelAPI_Attribute> mySelectedAttribute; /// cashed selected attribute
   std::shared_ptr<GeomAPI_Pnt2d> myClickedSketchPoint; /// cashed clicked point
+
+  bool myIsAutoConstraints;
 };
 
 #endif
index 221207f9d910ebb8b42d124d0f4ef273ca30a160..dafd2526ff3e44a57c511dcf38c9b1100d148f8f 100644 (file)
@@ -21,6 +21,7 @@
 #include "PartSet_Tools.h"
 #include "PartSet_Module.h"
 #include "PartSet_PreviewPlanes.h"
+#include "PartSet_SketcherReentrantMgr.h"
 
 #include "SketchPlugin_SketchEntity.h"
 
@@ -171,6 +172,10 @@ myIsSelection(false)
   connect(myShowPoints, SIGNAL(toggled(bool)), this, SIGNAL(showFreePoints(bool)));
   aLayout->addWidget(myShowPoints);
 
+  myAutoConstraints = new QCheckBox(tr("Automatic constraints"), this);
+  connect(myAutoConstraints, SIGNAL(toggled(bool)), this, SIGNAL(autoConstraints(bool)));
+  aLayout->addWidget(myAutoConstraints);
+
   QPushButton* aPlaneBtn = new QPushButton(tr("Change sketch plane"), aSecondWgt);
   connect(aPlaneBtn, SIGNAL(clicked(bool)), SLOT(onChangePlane()));
   aLayout->addWidget(aPlaneBtn);
@@ -508,6 +513,13 @@ bool PartSet_WidgetSketchLabel::fillSketchPlaneBySelection(const ModuleBase_View
 
 void PartSet_WidgetSketchLabel::activateCustom()
 {
+  PartSet_Module* aModule = dynamic_cast<PartSet_Module*>(myWorkshop->module());
+  if (aModule) {
+    bool isBlocked = myAutoConstraints->blockSignals(true);
+    myAutoConstraints->setChecked(aModule->sketchReentranceMgr()->isAutoConstraints());
+    myAutoConstraints->blockSignals(isBlocked);
+  }
+
   std::shared_ptr<GeomAPI_Pln> aPlane = plane();
   if (aPlane.get()) {
     myStackWidget->setCurrentIndex(1);
index 74a16f2248f33199e47f8d54c402636130499e05..b4158194bf4f8947b666b5cf621aeb5d9f63086c 100644 (file)
@@ -118,8 +118,14 @@ signals:
   /// \param theState a state of the check box
   void showConstraintToggled(int theType, bool theState);
 
+  /// The signal is emitted when user checks "Show free points" button
+  /// \param toShow a state of the check box
   void showFreePoints(bool toShow);
 
+  /// The signal is emitted when user checks "Automatic constraints" button
+  /// \param isOn a state of the check box
+  void autoConstraints(bool isOn);
+
 protected:
   /// Creates a backup of the current values of the attribute
   /// It should be realized in the specific widget because of different
@@ -223,6 +229,7 @@ private:
   QCheckBox* myViewInverted;
   QCheckBox* myRemoveExternal;
   QCheckBox* myShowPoints;
+  QCheckBox* myAutoConstraints;
 
   QMap<PartSet_Tools::ConstraintVisibleState, QCheckBox*> myShowConstraints;
 
index 4bad39fefc6e56952ef3bb263bccb97807fc41a7..67dc5f0c768bcb6063d9cb07566d4cfa7aa4542e 100644 (file)
@@ -201,3 +201,29 @@ void SketchAPI_MacroArc::setByTangent(const ModelHighAPI_RefAttr& theTangentPoin
 
   execute();
 }
+
+//================================================================================================
+void SketchAPI_MacroArc::setByTransversal(const ModelHighAPI_RefAttr& theTransversalPoint,
+                                          double theEndX, double theEndY,
+                                          bool theInversed)
+{
+  fillAttribute(SketchPlugin_MacroArc::ARC_TYPE_BY_TRANSVERSAL_LINE(), myarcType);
+  fillAttribute(theTransversalPoint, mytangentPoint);
+  fillAttribute(endPoint3(), theEndX, theEndY);
+  fillAttribute(theInversed, myreversed);
+
+  execute();
+}
+
+//================================================================================================
+void SketchAPI_MacroArc::setByTransversal(const ModelHighAPI_RefAttr& theTransversalPoint,
+                                          const std::shared_ptr<GeomAPI_Pnt2d>& theEnd,
+                                          bool theInversed)
+{
+  fillAttribute(SketchPlugin_MacroArc::ARC_TYPE_BY_TRANSVERSAL_LINE(), myarcType);
+  fillAttribute(theTransversalPoint, mytangentPoint);
+  fillAttribute(theEnd, myendPoint3);
+  fillAttribute(theInversed, myreversed);
+
+  execute();
+}
index a4b51ccec3371c608df32034166edab32e63b50c..3a6ae556604c73c01c28ae6110ae009519603f15 100644 (file)
@@ -114,8 +114,6 @@ public:
                angle, SketchPlugin_MacroArc::ANGLE_ID(),
                ModelAPI_AttributeDouble, /** Angle */)
 
-private:
-
   /// Set by center and start, end point.
   SKETCHAPI_EXPORT
   void setByCenterStartEnd(double theCenterX, double theCenterY,
@@ -153,6 +151,18 @@ private:
   void setByTangent(const ModelHighAPI_RefAttr& theTangentPoint,
                     const std::shared_ptr<GeomAPI_Pnt2d>& theEnd,
                     bool theInversed);
+
+  /// Set by tangent and end point.
+  SKETCHAPI_EXPORT
+  void setByTransversal(const ModelHighAPI_RefAttr& theTransversalPoint,
+                        double theEndX, double theEndY,
+                        bool theInversed);
+
+  /// Set by tangent and end point.
+  SKETCHAPI_EXPORT
+  void setByTransversal(const ModelHighAPI_RefAttr& theTransversalPoint,
+                        const std::shared_ptr<GeomAPI_Pnt2d>& theEnd,
+                        bool theInversed);
 };
 
 /// Pointer on Arc object.
index 722cbbaa77e40782003c6940d9f14d61f867801a..e1aabba8adf3eac43c6e913336d0c696425389bc 100644 (file)
@@ -523,22 +523,33 @@ std::shared_ptr<SketchAPI_MacroArc> SketchAPI_Sketch::addArc(
 std::shared_ptr<SketchAPI_MacroArc> SketchAPI_Sketch::addArc(
                                                 const ModelHighAPI_RefAttr& theTangentPoint,
                                                 double theEndX, double theEndY,
-                                                bool theInversed)
+                                                bool theInversed,
+                                                bool theTransversal)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
     compositeFeature()->addFeature(SketchPlugin_MacroArc::ID());
-  return MacroArcPtr(new SketchAPI_MacroArc(
-    aFeature, theTangentPoint, theEndX, theEndY, theInversed));
+  MacroArcPtr aMacroArc(new SketchAPI_MacroArc(aFeature));
+  if (theTransversal)
+    aMacroArc->setByTransversal(theTangentPoint, theEndX, theEndY, theInversed);
+  else
+    aMacroArc->setByTangent(theTangentPoint, theEndX, theEndY, theInversed);
+  return aMacroArc;
 }
 
 std::shared_ptr<SketchAPI_MacroArc> SketchAPI_Sketch::addArc(
                                               const ModelHighAPI_RefAttr& theTangentPoint,
                                               const std::shared_ptr<GeomAPI_Pnt2d>& theEnd,
-                                              bool theInversed)
+                                              bool theInversed,
+                                              bool theTransversal)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
     compositeFeature()->addFeature(SketchPlugin_MacroArc::ID());
-  return MacroArcPtr(new SketchAPI_MacroArc(aFeature, theTangentPoint, theEnd, theInversed));
+  MacroArcPtr aMacroArc(new SketchAPI_MacroArc(aFeature));
+  if (theTransversal)
+    aMacroArc->setByTransversal(theTangentPoint, theEnd, theInversed);
+  else
+    aMacroArc->setByTangent(theTangentPoint, theEnd, theInversed);
+  return aMacroArc;
 }
 
 std::shared_ptr<SketchAPI_Arc> SketchAPI_Sketch::addArc(const ModelHighAPI_Selection & theExternal)
index c32c88d9daf73dc0980d559d2988f126f56a2f10..180ea15bb7f2f7a7ac6d8e394e712bb28234c6a3 100644 (file)
@@ -240,19 +240,21 @@ public:
       const std::shared_ptr<GeomAPI_Pnt2d>& theEnd,
       const std::shared_ptr<GeomAPI_Pnt2d>& thePassed);
 
-  /// Add arc
+  /// Add transversal/tangent arc
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_MacroArc> addArc(
-      const ModelHighAPI_RefAttr& theTangentPoint,
+      const ModelHighAPI_RefAttr& theConnectedPoint,
       double theEndX, double theEndY,
-      bool theInversed);
+      bool theInversed,
+      bool theTransversal = false);
 
-  /// Add arc
+  /// Add transversal/tangent arc
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_MacroArc> addArc(
-      const ModelHighAPI_RefAttr& theTangentPoint,
+      const ModelHighAPI_RefAttr& theConnectedPoint,
       const std::shared_ptr<GeomAPI_Pnt2d>& theEnd,
-      bool theInversed);
+      bool theInversed,
+      bool theTransversal = false);
 
   /// Add arc
   SKETCHAPI_EXPORT
index d72e0c56bf28bd1a262e4d8064f2d56425048513..26cf523f22870505faa102b59b51d12666ab0fd9 100644 (file)
@@ -212,8 +212,10 @@ ADD_UNIT_TESTS(
   TestConstraintHorizontalValidator.py
   TestConstraintLength.py
   TestConstraintMiddlePoint.py
+  TestConstraintMiddlePointOnArc.py
   TestConstraintParallel.py
   TestConstraintPerpendicular.py
+  TestConstraintPerpendicularArcLine.py
   TestConstraintRadius.py
   TestConstraintRadiusFailure.py
   TestConstraintTangent.py
@@ -221,6 +223,7 @@ ADD_UNIT_TESTS(
   TestCreateArcByCenterStartEnd.py
   TestCreateArcByTangentEdge.py
   TestCreateArcByThreePoints.py
+  TestCreateArcByTransversalLine.py
   TestCreateArcChangeType.py
   TestCreateCircleByCenterAndPassed.py
   TestCreateCircleByThreePoints.py
index 3e6327e08ef04fc5a9bc844f1018048a0014266d..f0379c4618eac92385f15b151b3f1eaedd048e20 100644 (file)
@@ -20,6 +20,7 @@
 #include "SketchPlugin_MacroArc.h"
 
 #include "SketchPlugin_Arc.h"
+#include "SketchPlugin_ConstraintPerpendicular.h"
 #include "SketchPlugin_ConstraintTangent.h"
 #include "SketchPlugin_Sketch.h"
 #include "SketchPlugin_Tools.h"
@@ -209,7 +210,9 @@ void SketchPlugin_MacroArc::attributeChanged(const std::string& theID)
   else if(anArcType == ARC_TYPE_BY_THREE_POINTS())
     fillByThreePassedPoints();
   else if(anArcType == ARC_TYPE_BY_TANGENT_EDGE())
-    fillByTangentEdge();
+    fillByEdge(false);
+  else if (anArcType == ARC_TYPE_BY_TRANSVERSAL_LINE())
+    fillByEdge(true);
 
   double aRadius = 0;
   double anAngle = 0;
@@ -339,19 +342,27 @@ void SketchPlugin_MacroArc::execute()
                                          AttributePtr(),
                                          anArcFeature->lastResult(),
                                          true);
-  } else if(anArcType == ARC_TYPE_BY_TANGENT_EDGE()) {
-    // constraints for tangent arc
+  } else {
+    // coincident with connection point
     SketchPlugin_Tools::createCoincidenceOrTangency(this,
                                          TANGENT_POINT_ID(),
                                          anArcFeature->attribute(SketchPlugin_Arc::START_ID()),
                                          ObjectPtr(),
                                          false);
-    FeaturePtr aTangent = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
-    AttributeRefAttrPtr aRefAttrA = aTangent->refattr(SketchPlugin_Constraint::ENTITY_A());
+    // tangent or perpendicular constraint
+    FeaturePtr aStartPointConstraint;
+    if (anArcType == ARC_TYPE_BY_TANGENT_EDGE())
+      aStartPointConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
+    else
+      aStartPointConstraint = sketch()->addFeature(SketchPlugin_ConstraintPerpendicular::ID());
+    // setting attributes of the start point constraint
+    AttributeRefAttrPtr aRefAttrA =
+        aStartPointConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
     AttributeRefAttrPtr aTgPntRefAttr = refattr(TANGENT_POINT_ID());
     FeaturePtr aTgFeature = ModelAPI_Feature::feature(aTgPntRefAttr->attr()->owner());
     aRefAttrA->setObject(aTgFeature->lastResult());
-    AttributeRefAttrPtr aRefAttrB = aTangent->refattr(SketchPlugin_Constraint::ENTITY_B());
+    AttributeRefAttrPtr aRefAttrB =
+        aStartPointConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
     aRefAttrB->setObject(anArcFeature->lastResult());
     // constraint for end point
     SketchPlugin_Tools::createCoincidenceOrTangency(this,
@@ -596,15 +607,15 @@ void SketchPlugin_MacroArc::recalculateReversedFlagByPassed(
   myParamBefore = aEndParam;
 }
 
-void SketchPlugin_MacroArc::fillByTangentEdge()
+void SketchPlugin_MacroArc::fillByEdge(bool theTransversal)
 {
   AttributeRefAttrPtr aTangentAttr = refattr(TANGENT_POINT_ID());
   if (!aTangentAttr->isInitialized())
     return;
 
-  AttributePoint2DPtr aTangentPointAttr =
+  AttributePoint2DPtr aConnectionPointAttr =
       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aTangentAttr->attr());
-  if (!aTangentPointAttr->isInitialized())
+  if (!aConnectionPointAttr->isInitialized())
     return;
 
   AttributePoint2DPtr anEndPointAttr =
@@ -612,19 +623,22 @@ void SketchPlugin_MacroArc::fillByTangentEdge()
   if (!anEndPointAttr->isInitialized())
     return;
 
-  myStart = aTangentPointAttr->pnt();
+  myStart = aConnectionPointAttr->pnt();
   myEnd = anEndPointAttr->pnt();
   if (myStart->isEqual(myEnd))
     return;
 
   // obtain a shape the tangent point belongs to
-  FeaturePtr aTangentFeature = ModelAPI_Feature::feature(aTangentPointAttr->owner());
-  std::shared_ptr<GeomAPI_Shape> aTangentShape = aTangentFeature->lastResult()->shape();
+  FeaturePtr aConnectedFeature = ModelAPI_Feature::feature(aConnectionPointAttr->owner());
+  std::shared_ptr<GeomAPI_Shape> aTangentShape = aConnectedFeature->lastResult()->shape();
 
   GeomAlgoAPI_Circ2dBuilder aCircBuilder(SketchPlugin_Sketch::plane(sketch()));
   aCircBuilder.addPassingPoint(myStart);
   aCircBuilder.addPassingPoint(myEnd);
-  aCircBuilder.addTangentCurve(aTangentShape);
+  if (theTransversal)
+    aCircBuilder.setTransversalLine(aTangentShape);
+  else
+    aCircBuilder.addTangentCurve(aTangentShape);
 
   std::shared_ptr<GeomAPI_Circ2d> aCircle = aCircBuilder.circle();
   if (!aCircle)
index 815cd7002fce88c7d4f6f47a28a48734396b6983..886f6efdcb91b8de25eb0e88f30d254c6ddf47c5 100644 (file)
@@ -72,6 +72,12 @@ class SketchPlugin_MacroArc: public SketchPlugin_SketchEntity,
     return ID;
   }
 
+  inline static const std::string& ARC_TYPE_BY_TRANSVERSAL_LINE()
+  {
+    static const std::string ID("by_transversal_line");
+    return ID;
+  }
+
   /// Central 2D point of the circle which contains the arc
   inline static const std::string& CENTER_POINT_ID()
   {
@@ -220,8 +226,9 @@ private:
   void fillByCenterAndTwoPassed();
   /// Set fields for center, start and end points by selected passed points
   void fillByThreePassedPoints();
-  /// Set fields for center, start and end points by selected tangent edge
-  void fillByTangentEdge();
+  /// Set fields for center, start and end points by selected tangent or transversal edge
+  /// \param theTransversal if \c true, builds transversal arc, otherwise builds tangential arc.
+  void fillByEdge(bool theTransversal);
 
   FeaturePtr createArcFeature();
 
index c9a10a82b2801a132a3c2d12c82d2f3ed35d855c..67d4e4656695938703899b92bf24ecd2ef18f0ae 100644 (file)
@@ -95,6 +95,8 @@ SketchPlugin_Plugin::SketchPlugin_Plugin()
                               new SketchPlugin_ExternalValidator);
   aFactory->registerValidator("SketchPlugin_TangentAttr",
                               new SketchPlugin_TangentAttrValidator);
+  aFactory->registerValidator("SketchPlugin_PerpendicularAttr",
+                              new SketchPlugin_PerpendicularAttrValidator);
   aFactory->registerValidator("SketchPlugin_NotFixed",
                               new SketchPlugin_NotFixedValidator);
   aFactory->registerValidator("SketchPlugin_EqualAttr",
@@ -117,6 +119,8 @@ SketchPlugin_Plugin::SketchPlugin_Plugin()
                               new SketchPlugin_MiddlePointAttrValidator);
   aFactory->registerValidator("SketchPlugin_ArcTangentPoint",
                               new SketchPlugin_ArcTangentPointValidator);
+  aFactory->registerValidator("SketchPlugin_ArcTransversalPoint",
+                              new SketchPlugin_ArcTransversalPointValidator);
   aFactory->registerValidator("SketchPlugin_IntersectionValidator",
                               new SketchPlugin_IntersectionValidator);
   aFactory->registerValidator("SketchPlugin_ProjectionValidator",
index 8962cf083547a98ea3e05ad598b70b4bd22fafb9..fb9be5197ed7a6dcf6272367797871a077953655 100644 (file)
@@ -198,6 +198,50 @@ bool SketchPlugin_TangentAttrValidator::isValid(const AttributePtr& theAttribute
   return true;
 }
 
+bool SketchPlugin_PerpendicularAttrValidator::isValid(const AttributePtr& theAttribute,
+                                                      const std::list<std::string>& theArguments,
+                                                      Events_InfoMessage& theError) const
+{
+  if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
+    theError = "The attribute with the %1 type is not processed";
+    theError.arg(theAttribute->attributeType());
+    return false;
+  }
+
+  std::string aParamA = theArguments.front();
+  SessionPtr aMgr = ModelAPI_Session::get();
+  ModelAPI_ValidatorsFactory* aFactory = aMgr->validators();
+
+  FeaturePtr anOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
+  AttributeRefAttrPtr aRefAttr =
+      std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
+
+  bool isObject = aRefAttr->isObject();
+  ObjectPtr anObject = aRefAttr->object();
+  if (isObject && anObject.get()) {
+    FeaturePtr aRefFea = ModelAPI_Feature::feature(anObject);
+
+    AttributeRefAttrPtr aOtherAttr = anOwner->refattr(aParamA);
+    ObjectPtr aOtherObject = aOtherAttr->object();
+    FeaturePtr aOtherFea = ModelAPI_Feature::feature(aOtherObject);
+    if (!aOtherFea)
+      return true;
+
+    // at least one feature should be a line
+    if (aRefFea->getKind() != SketchPlugin_Line::ID() &&
+        aOtherFea->getKind() != SketchPlugin_Line::ID()) {
+      theError = "At least one feature should be a line";
+      return false;
+    }
+  }
+  else {
+    theError = "It uses an empty object";
+    return false;
+  }
+
+  return true;
+}
+
 bool SketchPlugin_NotFixedValidator::isValid(const AttributePtr& theAttribute,
                                              const std::list<std::string>& theArguments,
                                              Events_InfoMessage& theError) const
@@ -734,7 +778,8 @@ bool SketchPlugin_MiddlePointAttrValidator::isValid(const AttributePtr& theAttri
 
       if (aFeature->getKind() == SketchPlugin_Point::ID())
         ++aNbPoints;
-      else if (aFeature->getKind() == SketchPlugin_Line::ID())
+      else if (aFeature->getKind() == SketchPlugin_Line::ID() ||
+               aFeature->getKind() == SketchPlugin_Arc::ID())
         ++aNbLines;
     }
   }
@@ -792,6 +837,44 @@ bool SketchPlugin_ArcTangentPointValidator::isValid(const AttributePtr& theAttri
   return true;
 }
 
+bool SketchPlugin_ArcTransversalPointValidator::isValid(
+    const AttributePtr& theAttribute,
+    const std::list<std::string>& /*theArguments*/,
+    Events_InfoMessage& theError) const
+{
+  if (theAttribute->attributeType() != ModelAPI_AttributeRefAttr::typeId()) {
+    theError = "The attribute with the %1 type is not processed";
+    theError.arg(theAttribute->attributeType());
+    return false;
+  }
+  AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
+  AttributePtr anAttr = aRefAttr->attr();
+  if (!anAttr) {
+    theError = "The attribute %1 should be a point";
+    theError.arg(theAttribute->id());
+    return false;
+  }
+
+  FeaturePtr anAttrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
+  const std::string& aFeatureType = anAttrFeature->getKind();
+  if (aFeatureType == SketchPlugin_Line::ID()) {
+    // selected point should be bound point of line
+    const std::string& aPntId = anAttr->id();
+    if (aPntId != SketchPlugin_Line::START_ID() && aPntId != SketchPlugin_Line::END_ID()) {
+      theError = "The attribute %1 is not supported";
+      theError.arg(aPntId);
+      return false;
+    }
+  }
+  else {
+    theError = "Unable to build transversal arc on %1";
+    theError.arg(anAttrFeature->getKind());
+    return false;
+  }
+
+  return true;
+}
+
 bool SketchPlugin_IntersectionValidator::isValid(const AttributePtr& theAttribute,
                                                  const std::list<std::string>& theArguments,
                                                  Events_InfoMessage& theError) const
index 4a7bd052576a7952bb799ef1c3169c1a83e380c5..b3fe8f57464ee41781dcfe9d114f9caaac735a89 100644 (file)
@@ -60,6 +60,24 @@ class SketchPlugin_TangentAttrValidator : public ModelAPI_AttributeValidator
                        Events_InfoMessage& theError) const;
 };
 
+/**\class SketchPlugin_PerpendicularAttrValidator
+ * \ingroup Validators
+ * \brief Validator for the perpendicular constraint input.
+ *
+ * Checks that two arcs are not selected for perpendicular.
+ */
+class SketchPlugin_PerpendicularAttrValidator : public ModelAPI_AttributeValidator
+{
+ public:
+  //! returns true if attribute is valid
+  //! \param theAttribute the checked attribute
+  //! \param theArguments arguments of the attribute
+  //! \param theError error message
+  virtual bool isValid(const AttributePtr& theAttribute,
+                       const std::list<std::string>& theArguments,
+                       Events_InfoMessage& theError) const;
+};
+
 
 /**\class SketchPlugin_NotFixedValidator
  * \ingroup Validators
@@ -231,6 +249,25 @@ class SketchPlugin_ArcTangentPointValidator : public ModelAPI_AttributeValidator
                        Events_InfoMessage& theError) const;
 };
 
+/**\class SketchPlugin_ArcTransversalPointValidator
+ * \ingroup Validators
+ * \brief Validator for the point where the transversal arc is building.
+ *
+ * Checks that the point is a start or end point just on line or arc.
+ */
+class SketchPlugin_ArcTransversalPointValidator : public ModelAPI_AttributeValidator
+{
+ public:
+  //! returns true if attribute is valid
+  //! \param theAttribute the checked attribute
+  //! \param theArguments arguments of the attribute
+  //! \param theError error message
+  virtual bool isValid(const AttributePtr& theAttribute,
+                       const std::list<std::string>& theArguments,
+                       Events_InfoMessage& theError) const;
+};
+
+
 /**\class SketchPlugin_SplitValidator
  * \ingroup Validators
  * \brief Validator for the entity of the following type:
diff --git a/src/SketchPlugin/Test/TestConstraintMiddlePointOnArc.py b/src/SketchPlugin/Test/TestConstraintMiddlePointOnArc.py
new file mode 100644 (file)
index 0000000..2688932
--- /dev/null
@@ -0,0 +1,252 @@
+# Copyright (C) 2017-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
+#
+
+"""
+    Test middle point on an arc
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from GeomAPI import GeomAPI_Dir2d
+
+__updated__ = "2019-09-03"
+
+class TestMiddlePointOnArc(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myTestPassed = True
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myArc = self.mySketch.addArc(50, 50, 70, 50, 50, 70, False)
+    self.myLine = self.mySketch.addLine(55, 60, 50, 0)
+    self.myDOF = 9
+    model.do()
+    self.checkDOF()
+
+  def tearDown(self):
+    if self.myTestPassed:
+      model.assertArcValidity(self.myArc)
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      self.checkDOF()
+    model.end()
+    assert(model.checkPythonDump())
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkMiddlePoint(self, thePoint, theArc):
+    self.myTestPassed = False
+    # check point on arc
+    dist = thePoint.pnt().distance(theArc.center().pnt())
+    NB_DIGITS = 7 - math.floor(math.log10(theArc.radius().value()))
+    self.assertAlmostEqual(dist, theArc.radius().value(), NB_DIGITS)
+    # check middle point
+    dirPC = GeomAPI_Dir2d(thePoint.x() - theArc.center().x(),
+                          thePoint.y() - theArc.center().y())
+    dirSC = GeomAPI_Dir2d(theArc.startPoint().x() - theArc.center().x(),
+                          theArc.startPoint().y() - theArc.center().y())
+    dirEC = GeomAPI_Dir2d(theArc.endPoint().x() - theArc.center().x(),
+                          theArc.endPoint().y() - theArc.center().y())
+    angleSP = dirSC.angle(dirPC)
+    anglePE = dirPC.angle(dirEC)
+    self.assertAlmostEqual(angleSP, anglePE)
+    self.assertEqual(angleSP < 0, theArc.reversed().value())
+    self.myTestPassed = True
+
+  def rotatePoint(self, thePoint, theCenter, theAngle):
+    dirX = thePoint.x() - theCenter.x()
+    dirY = thePoint.y() - theCenter.y()
+    newX = theCenter.x() + dirX * math.cos(theAngle) - dirY * math.sin(theAngle)
+    newY = theCenter.y() + dirX * math.sin(theAngle) + dirY * math.cos(theAngle)
+    self.mySketch.move(thePoint, newX, newY)
+
+  def moveArc(self):
+    ANGLE_STEP = math.pi * 5.0 / 180.0
+    ANGLE_THRESHOLD = math.pi
+    # move start point of the arc clockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.startPoint(), self.myArc.center(), -ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+    # move start point of the arc conterclockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.startPoint(), self.myArc.center(), ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+
+    # move end point of the arc clockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.endPoint(), self.myArc.center(), -ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+    # move end point of the arc conterclockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.endPoint(), self.myArc.center(), ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+
+    # move center of the arc
+    DELTA = [1.0, 1.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myArc.center(), self.myArc.center().x() + DELTA[0], self.myArc.center().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    DELTA = [-1.0, 1.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myArc.center(), self.myArc.center().x() + DELTA[0], self.myArc.center().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+
+  def moveLine(self):
+    DELTA = [1.0, 0.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myLine.startPoint(), self.myLine.startPoint().x() + DELTA[0], self.myLine.startPoint().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    DELTA = [0.0, 1.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myLine.startPoint(), self.myLine.startPoint().x() + DELTA[0], self.myLine.startPoint().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+
+
+  def test_middle_point_PA(self):
+    """ Test 1. Set middle point constraint (point is the first argument)
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+
+  def test_middle_point_AP(self):
+    """ Test 2. Set middle point constraint (point is the second argument)
+    """
+    self.mySketch.setMiddlePoint(self.myArc.results()[1], self.myLine.startPoint())
+    self.myDOF -= 2
+    model.do()
+
+  def test_coincident_middle_point(self):
+    """ Test 3. Set middle point constraint for the point already coincident with the arc
+    """
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+
+  def test_middle_point_coincident(self):
+    """ Test 4. Set concidence of the point and the arc which are already constrained with middle point
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+
+  @unittest.expectedFailure
+  def test_middle_point_limitation(self):
+    """ Test 5. Check middle point fails if the point's coordinates are equal to the arc boundary point
+    """
+    self.myLine.startPoint().setValue(self.myArc.endPoint().pnt())
+    model.do()
+    coincidence = self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    # this check will fail due to the limitation of PlanGCS
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+
+  def test_middle_point_move_arc(self):
+    """ Test 6. Set middle point constraint and move arc
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveArc()
+
+  def test_middle_point_coincidence_move_arc(self):
+    """ Test 7. Set coincidence and middle point constraint and move arc
+    """
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveArc()
+
+  def test_middle_point_move_line(self):
+    """ Test 8. Set middle point constraint and move line
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveLine()
+
+  def test_middle_point_coincidence_move_line(self):
+    """ Test 9. Set coincidence and middle point constraint and move line
+    """
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveLine()
+
+  def test_remove_middle_point(self):
+    """ Test 10. Set and then remove middle point constraint
+    """
+    mp = self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    model.assertArcValidity(self.myArc)
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.checkDOF()
+    # remove middle point
+    self.myDocument.removeFeature(mp.feature())
+    self.myDOF += 2
+    model.do()
+    self.checkDOF()
+    # set flag False to avoid checking middle point constraint in tearDown() method
+    self.myTestPassed = False
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
diff --git a/src/SketchPlugin/Test/TestConstraintPerpendicularArcLine.py b/src/SketchPlugin/Test/TestConstraintPerpendicularArcLine.py
new file mode 100644 (file)
index 0000000..c0fd9b3
--- /dev/null
@@ -0,0 +1,54 @@
+# Copyright (C) 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
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(-35, -10, 20)
+SketchLine_1 = Sketch_1.addLine(-21, 2, 24, 21)
+SketchLine_2 = Sketch_1.addLine(24, 21, 20, -30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchArc_1 = Sketch_1.addArc(25, -30, 42, -34, 13, -17.31142245955048, False)
+model.do()
+
+# check error on perpendicularity of circle and arc
+SketchConstraintPerpendicular_1 = Sketch_1.setPerpendicular(SketchCircle_1.results()[1], SketchArc_1.results()[1])
+model.do()
+assert(SketchConstraintPerpendicular_1.feature().error() != "")
+
+# avoid the failure
+Part_1_doc.removeFeature(SketchConstraintPerpendicular_1.feature())
+model.do()
+assert(Sketch_1.feature().error() == "")
+
+# set correct constraints
+SketchConstraintPerpendicular_2 = Sketch_1.setPerpendicular(SketchCircle_1.results()[1], SketchLine_1.result())
+SketchConstraintPerpendicular_3 = Sketch_1.setPerpendicular(SketchLine_2.result(), SketchArc_1.results()[1])
+model.do()
+
+TOLERANCE = 1.e-7
+assert(model.distancePointLine(SketchCircle_1.center(), SketchLine_1) < TOLERANCE)
+assert(model.distancePointLine(SketchArc_1.center(), SketchLine_2) < TOLERANCE)
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestCreateArcByTransversalLine.py b/src/SketchPlugin/Test/TestCreateArcByTransversalLine.py
new file mode 100644 (file)
index 0000000..0250f95
--- /dev/null
@@ -0,0 +1,163 @@
+# 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
+#
+
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+from GeomDataAPI import *
+from ModelAPI import *
+from SketchAPI import SketchAPI_Sketch
+import math
+from salome.shaper import model
+
+__updated__ = "2019-08-16"
+
+TOLERANCE = 1.e-7
+
+#=========================================================================
+# Auxiliary functions
+#=========================================================================
+
+def verifyLastArc(theSketch, theCenter, theStart, theEnd):
+    """
+    subroutine to verify position of last arc in the sketch
+    """
+    aLastArc = model.lastSubFeature(theSketch, "SketchArc")
+    model.assertArc(aLastArc, theCenter, theStart, theEnd)
+
+def verifyArcLineTransversal(theArc, theLine):
+    aCenter = geomDataAPI_Point2D(theArc.attribute("center_point"))
+    aDistCL = model.distancePointLine(aCenter, theLine)
+    assert aDistCL < TOLERANCE, "Arc and line are not orthogonal"
+
+def verifyPointOnArc(thePoint, theArc):
+    aCenter = geomDataAPI_Point2D(theArc.attribute("center_point"))
+    aStart = geomDataAPI_Point2D(theArc.attribute("start_point"))
+    aRadius = model.distancePointPoint(aStart, aCenter)
+
+    aDistPP = model.distancePointPoint(aCenter, thePoint)
+    assert math.fabs(aRadius - aDistPP) < TOLERANCE, "Point is not on Circle, distance: {0}".format(aDistPP)
+
+
+
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchCommonFeature = aDocument.addFeature("Sketch")
+aSketchFeature = featureToCompositeFeature(aSketchCommonFeature)
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+aSketch = SketchAPI_Sketch(aSketchFeature)
+
+# auxiliary line
+aLineStartPnt = [0., 0.]
+aLineEndPnt = [50., 0.]
+aSession.startOperation()
+aSketchLine = aSketchFeature.addFeature("SketchLine")
+aLineStart = geomDataAPI_Point2D(aSketchLine.attribute("StartPoint"))
+aLineEnd = geomDataAPI_Point2D(aSketchLine.attribute("EndPoint"))
+aLineStart.setValue(aLineStartPnt[0], aLineStartPnt[1])
+aLineEnd.setValue(aLineEndPnt[0], aLineEndPnt[1])
+aSession.finishOperation()
+
+#=========================================================================
+# Test 1. Create an arc, orthogonal to the line
+#=========================================================================
+anArcEndPnt = [80., 20.]
+aSession.startOperation()
+anArc = aSketchFeature.addFeature("SketchMacroArc")
+assert (anArc.getKind() == "SketchMacroArc")
+anArcTgPnt = anArc.refattr("tangent_point")
+assert (not anArcTgPnt.isInitialized())
+anArcEnd = geomDataAPI_Point2D(anArc.attribute("end_point_3"))
+assert (not anArcEnd.isInitialized())
+anArcType = anArc.string("arc_type")
+assert (not anArcType.isInitialized())
+# initialize attributes
+anArcType.setValue("by_transversal_line")
+anArcTgPnt.setAttr(aLineEnd)
+anArcEnd.setValue(anArcEndPnt[0], anArcEndPnt[1])
+aSession.finishOperation()
+verifyLastArc(aSketchFeature, [], aLineEndPnt, anArcEndPnt)
+aLastArc = model.lastSubFeature(aSketchFeature, "SketchArc")
+verifyArcLineTransversal(aLastArc, aSketchLine)
+model.testNbSubFeatures(aSketch, "SketchConstraintCoincidence", 1)
+model.testNbSubFeatures(aSketch, "SketchConstraintPerpendicular", 1)
+
+#=========================================================================
+# Test 2. Create an arc, orthogonal to the previous arc (expect an error)
+#=========================================================================
+aPrevArc = aLastArc
+aPrevArcEnd = geomDataAPI_Point2D(aPrevArc.attribute("end_point"))
+anArcEndPnt = [50., 100.]
+aSession.startOperation()
+anArc = aSketchFeature.addFeature("SketchMacroArc")
+anArcTgPnt = anArc.refattr("tangent_point")
+anArcEnd = geomDataAPI_Point2D(anArc.attribute("end_point_3"))
+anArcType = anArc.string("arc_type")
+# initialize attributes
+anArcType.setValue("by_transversal_line")
+anArcTgPnt.setAttr(aPrevArcEnd)
+anArcEnd.setValue(anArcEndPnt[0], anArcEndPnt[1])
+aSession.finishOperation()
+assert(anArc.error() != "")
+# remove failed feature
+aSession.startOperation()
+aDocument.removeFeature(anArc)
+aSession.finishOperation()
+
+#=========================================================================
+# Test 3. Create an arc, orthogonal to the line with end point on the arc
+#=========================================================================
+aPrevArc = model.lastSubFeature(aSketchFeature, "SketchArc")
+aPrevArcEnd = geomDataAPI_Point2D(aPrevArc.attribute("end_point"))
+aSession.startOperation()
+anArc = aSketchFeature.addFeature("SketchMacroArc")
+anArcTgPnt = anArc.refattr("tangent_point")
+anArcEnd = geomDataAPI_Point2D(anArc.attribute("end_point_3"))
+anArcEndRef = anArc.refattr("end_point_ref")
+anArcType = anArc.string("arc_type")
+# initialize attributes
+anArcType.setValue("by_transversal_line")
+anArcTgPnt.setAttr(aLineStart)
+anArcEndRef.setObject(aPrevArc.lastResult())
+anArcEnd.setValue(anArcEndPnt[0], anArcEndPnt[1])
+aSession.finishOperation()
+verifyLastArc(aSketchFeature, [], [aLineStart.x(), aLineStart.y()], [])
+aLastArc = model.lastSubFeature(aSketchFeature, "SketchArc")
+verifyArcLineTransversal(aLastArc, aSketchLine)
+aLastArcEnd = geomDataAPI_Point2D(aLastArc.attribute("end_point"))
+verifyPointOnArc(aLastArcEnd, aPrevArc)
+model.testNbSubFeatures(aSketch, "SketchConstraintCoincidence", 3)
+model.testNbSubFeatures(aSketch, "SketchConstraintPerpendicular", 2)
+
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
index 9139ea4db9c7980ba2520635ec00bf43862c4150..b00034cbe5cea3ba6354702427aad3ed87b31f74 100644 (file)
@@ -10,7 +10,7 @@ To add a new Arc to the Sketch:
 #. select in the Main Menu *Sketch - > Arc* item  or
 #. click |arc.icon| **Arc** button in Sketch toolbar:
 
-There are 3 algorithms for creation of an Arc:
+There are 4 algorithms for creation of an Arc:
 
 .. image:: images/arc_base_32x32.png
    :align: left
@@ -24,6 +24,10 @@ There are 3 algorithms for creation of an Arc:
    :align: left
 **By tangent point and end point** creates an arc segment with the tangent point and the end point.
 
+.. image:: images/arc_perp_32x32.png
+   :align: left
+**By transversal point and end point** creates an arc segment perpendicular to a straight line with the start point, connected with boundary of this line, and the end point.
+
 By center and two points
 """"""""""""""""""""""""
 
@@ -90,12 +94,36 @@ The tangent point by itself is a start point. The edge on which it lies will be
 
 **TUI Command**:
 
-.. py:function:: Sketch_1.addArc(TangetPoint, EndX, EndY, Inversed)
+.. py:function:: Sketch_1.addArc(TangentPoint, EndX, EndY, Inversed)
+
+    :param object: Tangent Point.
+    :param real: End X.
+    :param real: End Y.
+    :param boolean: Is inversed.
+    :return: Result object.
+
+By transveral point and point
+"""""""""""""""""""""""""""""
+
+.. image:: images/Arc_panel_perp.png
+   :align: center
+
+Select a point on a straight segment in the view to set the transversal point, then move the mouse and click to set the end point.
+The transversal point by itself is a start point. The edge on which it lies will be perpendicular to the arc (the center of the arc is lying on the edge).
+
+- When entering a transversal point by selecting a point on segment, a Perpendicular constraint is created.
+- When entering an end point by selecting a segment, a Coincident constraint is created.
+- When entering an end point, only segments are selectable.
+
+**TUI Command**:
+
+.. py:function:: Sketch_1.addArc(TransversalPoint, EndX, EndY, Inversed, True)
 
-    :param object: Tanget Point.
+    :param object: Transversal Point.
     :param real: End X.
     :param real: End Y.
     :param boolean: Is inversed.
+    :param boolean: Arc is transversal (always True).
     :return: Result object.
 
 Result
index 3d44a951d86c41603bfaa186aa6298bc53d2abb7..cad2078bf9d72f7190a411dafd2f25a817b0ffc7 100644 (file)
@@ -10,5 +10,6 @@ SketchArc_2 = Sketch_1.addArc(8.1, 56.7, 58.2, 6.6, 44.8, 69.2)
 SketchLine_3 = Sketch_1.addLine(25.0, 109.4, 68.1, 153.6)
 SketchLine_3.setAuxiliary(True)
 SketchArc_3 = Sketch_1.addArc(SketchLine_3.startPoint(), 92.1, 34.0, True)
+SketchArc_4 = Sketch_1.addArc(SketchLine_3.endPoint(), 150.0, 10.0, True, True)
 model.do()
 model.end()
index 089d50730b22e133d05303bec7a93f95039ff42e..fd1b9aa8222b272545ae6b5234fda67fe2cffc7d 100644 (file)
Binary files a/src/SketchPlugin/doc/images/Arc_panel_3pt.png and b/src/SketchPlugin/doc/images/Arc_panel_3pt.png differ
index 9a5b9ed4a95cb5807180d49b6668a17cf2a36b7b..f702ca8bed48e2fd0eba6e80765590ff7ef3af5a 100644 (file)
Binary files a/src/SketchPlugin/doc/images/Arc_panel_base.png and b/src/SketchPlugin/doc/images/Arc_panel_base.png differ
diff --git a/src/SketchPlugin/doc/images/Arc_panel_perp.png b/src/SketchPlugin/doc/images/Arc_panel_perp.png
new file mode 100644 (file)
index 0000000..3e33cf9
Binary files /dev/null and b/src/SketchPlugin/doc/images/Arc_panel_perp.png differ
index ca02054011cb4b73ec2d6e950eda6e196622d55f..a14b891acaecd71f2a634254fdb70364cd604b42 100644 (file)
Binary files a/src/SketchPlugin/doc/images/Arc_panel_tang.png and b/src/SketchPlugin/doc/images/Arc_panel_tang.png differ
index b23a9022b44b0b5515292be17d51c26b7e8f31e4..1772eda3593471d5ad235c2c5cd31c86b6feae13 100644 (file)
Binary files a/src/SketchPlugin/doc/images/Arc_res.png and b/src/SketchPlugin/doc/images/Arc_res.png differ
index 0747cff0c00df58b083515f5d0e76c2046f22008..f1c0b928e1026e108a1e9ef08bda239aee007c14 100644 (file)
Binary files a/src/SketchPlugin/doc/images/Perpendicular_panel.png and b/src/SketchPlugin/doc/images/Perpendicular_panel.png differ
diff --git a/src/SketchPlugin/doc/images/arc_perp_32x32.png b/src/SketchPlugin/doc/images/arc_perp_32x32.png
new file mode 100644 (file)
index 0000000..56f9f83
Binary files /dev/null and b/src/SketchPlugin/doc/images/arc_perp_32x32.png differ
index 0cf532283f5edd4e571a22243b09692143f33836..dbcbc9b3b6efcff6aaa70229d43cd535a7aa3c59 100644 (file)
@@ -1,15 +1,16 @@
 .. _sketchPerpendicular:
-.. |perpendicular.icon|    image:: images/perpendicular.png
+.. |Perpendicular.icon|    image:: images/Perpendicular.png
 
 Perpendicular constraint
 ========================
 
 Perpendicular constraint fixes two lines at 90 degrees to one another.
+For a line and a circle/arc the perpendicular constraint fixes the center of circle/arc to be on a line.
 
 To create a Perpendicular constraint in the active Sketch:
 
 #. select in the Main Menu *Sketch - > Perpendicular* item  or
-#. click |perpendicular.icon| **Perpendicular** button in Sketch toolbar:
+#. click |Perpendicular.icon| **Perpendicular** button in Sketch toolbar:
 
 Property panel:
 
@@ -18,10 +19,12 @@ Property panel:
 
 Input fields:
 
-- **First line** is the first line selected in the view.
-- **Second line** is the second line selected in the view.
+- **First object** is the first line, circle or arc selected in the view.
+- **Second object** is the second line, circle or arc selected in the view.
 
-After the lines are selected, a special sign will be added to each of them in the view.
+After the objects are selected, a special sign will be added to each of them in the view.
+
+If one of selected objects is circular, then another has to be a straight line.
 
 **TUI Command**:
 
@@ -37,7 +40,7 @@ Result
 Created Perpendicular constraint appears in the view.
 
 .. image:: images/Perpendicular_res.png
-          :align: center
+           :align: center
 
 .. centered::
    Created perpendicular constraint
diff --git a/src/SketchPlugin/icons/arc_perp_32x32.png b/src/SketchPlugin/icons/arc_perp_32x32.png
new file mode 100644 (file)
index 0000000..56f9f83
Binary files /dev/null and b/src/SketchPlugin/icons/arc_perp_32x32.png differ
index 118bec3ac059912f37d713c125f5f615b27bac1d..89f8ddea1ee4948fdde866a75e75db11cd609bf9 100644 (file)
               <validator id="SketchPlugin_ArcEndPointValidator" parameters="end_point_ref"/>
             </sketch-2dpoint_selector>
           </box>
+          <box id="by_transversal_line"
+               icon="icons/Sketch/arc_perp_32x32.png"
+               title="Perpendicular to line">
+            <sketch_shape_selector id="tangent_point"
+                                   label="Transversal point"
+                                   tooltip="Select point on line"
+                                   shape_types="vertex">
+              <validator id="SketchPlugin_ArcTransversalPoint"/>
+            </sketch_shape_selector>
+            <sketch-2dpoint_selector id="end_point_3"
+                                     reference_attribute="end_point_ref"
+                                     title="End point"
+                                     tooltip="End point"
+                                     accept_expressions="0"
+                                     enable_value="enable_by_preferences">
+              <validator id="SketchPlugin_ArcEndPointValidator" parameters="end_point_ref"/>
+            </sketch-2dpoint_selector>
+          </box>
         </toolbox>
         <labelvalue id="radius"
                     icon="icons/Sketch/radius.png"
     </group>
 
     <group id="Dimensional constraints">
-    <!--  SketchConstraintDistance  -->
+      <!--  SketchConstraintDistance  -->
       <feature
         id="SketchConstraintDistance"
         title="Distance"
         <validator id="PartSet_ParallelSelection"/>
       </feature>
 
-    <!--  SketchConstraintPerpendicular  -->
+      <!--  SketchConstraintPerpendicular  -->
       <feature id="SketchConstraintPerpendicular" title="Perpendicular"
-               tooltip="Create constraint defining two perpendicular lines"
+               tooltip="Create constraint defining two orthogonal objects"
                icon="icons/Sketch/perpendicular.png"
                helpfile="perpendicularFeature.html">
         <sketch_shape_selector id="ConstraintEntityA"
-            label="First line" tooltip="Select a line"
+            label="First object" tooltip="Select line or arc"
             shape_types="edge">
           <validator id="PartSet_DifferentObjects"/>
           <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityB"/>
-            <validator id="GeomValidators_ShapeType" parameters="line"/>
+          <validator id="SketchPlugin_PerpendicularAttr" parameters="ConstraintEntityB"/>
+          <validator id="GeomValidators_ShapeType" parameters="line,circle"/>
         </sketch_shape_selector>
 
         <sketch_shape_selector id="ConstraintEntityB"
-            label="Second line" tooltip="Select a line"
+            label="Second object" tooltip="Select line or arc"
             shape_types="edge">
-            <validator id="PartSet_DifferentObjects"/>
+          <validator id="PartSet_DifferentObjects"/>
           <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityA"/>
-            <validator id="GeomValidators_ShapeType" parameters="line"/>
+          <validator id="SketchPlugin_PerpendicularAttr" parameters="ConstraintEntityA"/>
+          <validator id="GeomValidators_ShapeType" parameters="line,circle"/>
         </sketch_shape_selector>
         <validator id="PartSet_PerpendicularSelection"/>
       </feature>
         <validator id="PartSet_EqualSelection"/>
       </feature>
 
-    <!--  SketchConstraintCollinear  -->
+      <!--  SketchConstraintCollinear  -->
       <feature id="SketchConstraintCollinear" title="Collinear" tooltip="Create constraint defining collinearity of two lines"
                icon="icons/Sketch/collinear.png"
                helpfile="collinearFeature.html">
index 7a961fcad378ae7db1a3048a2b30258e77514f20..af72c4b13ae4870766f82a8dec15ce2b84fdfcda 100644 (file)
@@ -29,6 +29,7 @@ SET(PLANEGCSSOLVER_HEADERS
     PlaneGCSSolver_PointWrapper.h
     PlaneGCSSolver_ScalarWrapper.h
     PlaneGCSSolver_AngleWrapper.h
+    PlaneGCSSolver_BooleanWrapper.h
     PlaneGCSSolver_Tools.h
 )
 
@@ -40,6 +41,7 @@ SET(PLANEGCSSOLVER_SOURCES
     PlaneGCSSolver_PointWrapper.cpp
     PlaneGCSSolver_ScalarWrapper.cpp
     PlaneGCSSolver_AngleWrapper.cpp
+    PlaneGCSSolver_BooleanWrapper.cpp
     PlaneGCSSolver_Tools.cpp
 )
 
index ea6eef78c0d289b9c0f6b0b698f5966c46bdf60a..ca463c53d70f063745d9f062bf7c250cc6810d24 100644 (file)
@@ -21,6 +21,7 @@
 #include <PlaneGCSSolver_AttributeBuilder.h>
 #include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 
 #include <GeomDataAPI_Point2D.h>
 #include <ModelAPI_AttributeDouble.h>
@@ -44,6 +45,16 @@ static double* createParameter(PlaneGCSSolver_Storage* theStorage)
   return theStorage ? theStorage->createParameter() : (new double(0));
 }
 
+static EntityWrapperPtr createBoolean(const AttributePtr& theAttribute)
+{
+  BooleanWrapperPtr aWrapper;
+  AttributeBooleanPtr aBoolean =
+      std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
+  if (aBoolean)
+    aWrapper = BooleanWrapperPtr(new PlaneGCSSolver_BooleanWrapper(aBoolean->value()));
+  return aWrapper;
+}
+
 static EntityWrapperPtr createScalar(const AttributePtr&     theAttribute,
                                      PlaneGCSSolver_Storage* theStorage)
 {
@@ -99,6 +110,8 @@ EntityWrapperPtr PlaneGCSSolver_AttributeBuilder::createAttribute(
     aResult = createPoint(theAttribute, myStorage);
   if (!aResult)
     aResult = createScalar(theAttribute, myStorage);
+  if (!aResult)
+    aResult = createBoolean(theAttribute);
   if (aResult && !myStorage)
     aResult->setExternal(true);
   return aResult;
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.cpp
new file mode 100644 (file)
index 0000000..225c434
--- /dev/null
@@ -0,0 +1,25 @@
+// 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 <PlaneGCSSolver_BooleanWrapper.h>
+
+PlaneGCSSolver_BooleanWrapper::PlaneGCSSolver_BooleanWrapper(bool theParam)
+  : myValue(theParam)
+{
+}
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.h
new file mode 100644 (file)
index 0000000..3bd1d8d
--- /dev/null
@@ -0,0 +1,51 @@
+// 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
+//
+
+#ifndef PlaneGCSSolver_BooleanWrapper_H_
+#define PlaneGCSSolver_BooleanWrapper_H_
+
+#include <PlaneGCSSolver_Defs.h>
+#include <PlaneGCSSolver_EntityWrapper.h>
+
+/**
+ *  Wrapper providing storage for boolean values.
+ */
+class PlaneGCSSolver_BooleanWrapper : public PlaneGCSSolver_EntityWrapper
+{
+public:
+  PlaneGCSSolver_BooleanWrapper(bool theValue);
+
+  /// \brief Change value of parameter
+  void setValue(bool theValue)
+  { myValue = theValue; }
+  /// \brief Return value of parameter
+  bool value() const
+  { return myValue; }
+
+  /// \brief Return type of current entity
+  virtual SketchSolver_EntityType type() const
+  { return ENTITY_BOOLEAN; }
+
+protected:
+  bool myValue;
+};
+
+typedef std::shared_ptr<PlaneGCSSolver_BooleanWrapper> BooleanWrapperPtr;
+
+#endif
index fccd4d718b0cea8c7dd3eeb43275825a3f3fef60..e4851a295ecabfb4ef47d728373ed6a2874ef584 100644 (file)
@@ -44,6 +44,7 @@ const ConstraintID CID_FICTIVE = 1024;
 /// Types of entities
 enum SketchSolver_EntityType {
   ENTITY_UNKNOWN = 0,
+  ENTITY_BOOLEAN,
   ENTITY_SCALAR,
   ENTITY_ANGLE,
   ENTITY_POINT,
index 9cd5bc2cbe972483cda4a46702b463805656df6c..e84b809c60ee26d62a52bc8422c42d0e76f9d86c 100644 (file)
@@ -21,6 +21,7 @@
 #define PlaneGCSSolver_EdgeWrapper_H_
 
 #include <PlaneGCSSolver_Defs.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 #include <PlaneGCSSolver_EntityWrapper.h>
 
 /**
@@ -44,9 +45,16 @@ public:
 
   bool isDegenerated() const;
 
+  void setReversed(BooleanWrapperPtr theReversed)
+  { myReversed = theReversed; }
+
+  bool isReversed() const
+  { return myReversed ? myReversed->value() : false; }
+
 private:
   SketchSolver_EntityType myType;
   GCSCurvePtr myEntity;
+  BooleanWrapperPtr myReversed; // preferably used to control arc orientation
 };
 
 typedef std::shared_ptr<PlaneGCSSolver_EdgeWrapper> EdgeWrapperPtr;
index c2965e3eed652e0ada68cbee33756ff294a85b8d..d491a00490cda396fd0909b8ad7a105e97f2b39b 100644 (file)
@@ -21,6 +21,7 @@
 #include <PlaneGCSSolver_EdgeWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 
 #include <SketchPlugin_Arc.h>
 #include <SketchPlugin_Circle.h>
@@ -162,21 +163,25 @@ EntityWrapperPtr createArc(const AttributeEntityMap&    theAttributes,
                            PlaneGCSSolver_Storage*      theStorage)
 {
   std::shared_ptr<GCS::Arc> aNewArc(new GCS::Arc);
+  BooleanWrapperPtr isReversed;
 
   // Base attributes of arc (center, start and end points)
   AttributeEntityMap::const_iterator anIt = theAttributes.begin();
   for (; anIt != theAttributes.end(); ++anIt) {
     std::shared_ptr<PlaneGCSSolver_PointWrapper> aPoint =
         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(anIt->second);
-    if (!aPoint)
-      continue;
-
-    if (anIt->first->id() == SketchPlugin_Arc::CENTER_ID())
-      aNewArc->center = *(aPoint->point());
-    else if (anIt->first->id() == SketchPlugin_Arc::START_ID())
-      aNewArc->start = *(aPoint->point());
-    else if (anIt->first->id() == SketchPlugin_Arc::END_ID())
-      aNewArc->end = *(aPoint->point());
+    if (aPoint) {
+      if (anIt->first->id() == SketchPlugin_Arc::CENTER_ID())
+        aNewArc->center = *(aPoint->point());
+      else if (anIt->first->id() == SketchPlugin_Arc::START_ID())
+        aNewArc->start = *(aPoint->point());
+      else if (anIt->first->id() == SketchPlugin_Arc::END_ID())
+        aNewArc->end = *(aPoint->point());
+    }
+    else {
+      // reversed flag
+      isReversed = std::dynamic_pointer_cast<PlaneGCSSolver_BooleanWrapper>(anIt->second);
+    }
   }
 
   // Additional atrtributes of arc necessary for PlaneGCS solver
@@ -200,7 +205,9 @@ EntityWrapperPtr createArc(const AttributeEntityMap&    theAttributes,
       new GeomAPI_Dir2d((*aNewArc->end.x) - aCenter->x(), (*aNewArc->end.y) - aCenter->y()));
   *aNewArc->endAngle = OX->angle(aDir);
 
-  return EntityWrapperPtr(new PlaneGCSSolver_EdgeWrapper(aNewArc));
+  EdgeWrapperPtr anArcWrapper(new PlaneGCSSolver_EdgeWrapper(aNewArc));
+  anArcWrapper->setReversed(isReversed);
+  return anArcWrapper;
 }
 
 bool isAttributeApplicable(const std::string& theAttrName, const std::string& theOwnerName)
@@ -208,7 +215,8 @@ bool isAttributeApplicable(const std::string& theAttrName, const std::string& th
   if (theOwnerName == SketchPlugin_Arc::ID()) {
     return theAttrName == SketchPlugin_Arc::CENTER_ID() ||
            theAttrName == SketchPlugin_Arc::START_ID() ||
-           theAttrName == SketchPlugin_Arc::END_ID();
+           theAttrName == SketchPlugin_Arc::END_ID() ||
+           theAttrName == SketchPlugin_Arc::REVERSED_ID();
   }
   else if (theOwnerName == SketchPlugin_Circle::ID()) {
     return theAttrName == SketchPlugin_Circle::CENTER_ID() ||
index 2f98c88138f5b3814eea5ddea799ec05508d6c5c..5c8cebe53ff3a75fc0fa3ac47e2419adc3867411 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <PlaneGCSSolver_Storage.h>
 #include <PlaneGCSSolver_Solver.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 #include <PlaneGCSSolver_ConstraintWrapper.h>
 #include <PlaneGCSSolver_EdgeWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
@@ -134,6 +135,15 @@ static bool updateValues(AttributePtr& theAttribute, EntityWrapperPtr& theEntity
       isUpdated = updateValue(aScalar->value(), aValue);
       if (isUpdated)
         aWrapper->setValue(aValue);
+    } else {
+      AttributeBooleanPtr aBoolean =
+          std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
+      if (aBoolean) {
+        BooleanWrapperPtr aWrapper =
+            std::dynamic_pointer_cast<PlaneGCSSolver_BooleanWrapper>(theEntity);
+        isUpdated = aWrapper->value() != aBoolean->value();
+        aWrapper->setValue(aBoolean->value());
+      }
     }
   }
 
@@ -193,7 +203,8 @@ bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
   std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
   for (; anAttrIt != anAttributes.end(); ++anAttrIt)
     if ((*anAttrIt)->attributeType() == GeomDataAPI_Point2D::typeId() ||
-        (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId())
+        (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId() ||
+        (*anAttrIt)->attributeType() == ModelAPI_AttributeBoolean::typeId())
       isUpdated = update(*anAttrIt) || isUpdated;
 
   // check external attribute is changed
@@ -211,30 +222,6 @@ bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
   if (sendNotify && isUpdated)
     notify(theFeature);
 
-  // update arc
-  if (aRelated && aRelated->type() == ENTITY_ARC) {
-    /// TODO: this code should be shared with FeatureBuilder somehow
-
-    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> anEntity =
-        std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aRelated);
-    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEntity->entity());
-
-    static std::shared_ptr<GeomAPI_Dir2d> OX(new GeomAPI_Dir2d(1.0, 0.0));
-    std::shared_ptr<GeomAPI_Pnt2d> aCenter(
-        new GeomAPI_Pnt2d(*anArc->center.x, *anArc->center.y));
-    std::shared_ptr<GeomAPI_Pnt2d> aStart(
-        new GeomAPI_Pnt2d(*anArc->start.x, *anArc->start.y));
-
-    *anArc->rad = aStart->distance(aCenter);
-
-    std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(aStart->xy()->decreased(aCenter->xy())));
-    *anArc->startAngle = OX->angle(aDir);
-
-    aDir = std::shared_ptr<GeomAPI_Dir2d>(
-        new GeomAPI_Dir2d((*anArc->end.x) - aCenter->x(), (*anArc->end.y) - aCenter->y()));
-    *anArc->endAngle = OX->angle(aDir);
-  }
-
   return isUpdated;
 }
 
@@ -311,16 +298,16 @@ void PlaneGCSSolver_Storage::createArcConstraints(const EntityWrapperPtr& theArc
 
   // Additional constaints to fix arc's extra DoF (if the arc is not external):
   std::list<GCSConstraintPtr> anArcConstraints;
-  // 1. distances from center till start and end points are equal to radius
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
-      anArc->center, anArc->start, anArc->rad)));
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
-      anArc->center, anArc->end, anArc->rad)));
-  // 2. angles of start and end points should be equal to the arc angles
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
-      anArc->center, anArc->start, anArc->startAngle)));
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
-      anArc->center, anArc->end, anArc->endAngle)));
+  // constrain the start point on the arc
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->start, anArc->start.x, *anArc, anArc->startAngle)));
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->start, anArc->start.y, *anArc, anArc->startAngle)));
+  // constrain the end point on the arc
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->end, anArc->end.x, *anArc, anArc->endAngle)));
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->end, anArc->end.y, *anArc, anArc->endAngle)));
 
   ConstraintWrapperPtr aWrapper(
       new PlaneGCSSolver_ConstraintWrapper(anArcConstraints, CONSTRAINT_UNKNOWN));
@@ -340,6 +327,39 @@ void PlaneGCSSolver_Storage::removeArcConstraints(const EntityWrapperPtr& theArc
   }
 }
 
+void PlaneGCSSolver_Storage::adjustParametrizationOfArcs()
+{
+  std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator anIt = myArcConstraintMap.begin();
+  for (; anIt != myArcConstraintMap.end(); ++anIt) {
+    EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(anIt->first);
+    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
+    // tune start angle of the arc to be in [0, 2PI]
+    while (*anArc->startAngle < -PI)
+      *anArc->startAngle += 2.0 * PI;
+    while (*anArc->startAngle >= PI)
+      *anArc->startAngle -= 2.0 * PI;
+    // adjust end angle of the arc
+    if (anEdge->isReversed()) {
+      while (*anArc->endAngle > *anArc->startAngle)
+        *anArc->endAngle -= 2.0 * PI;
+      while (*anArc->endAngle + 2 * PI < *anArc->startAngle)
+        *anArc->endAngle += 2.0 * PI;
+    } else {
+      while (*anArc->endAngle < *anArc->startAngle)
+        *anArc->endAngle += 2.0 * PI;
+      while (*anArc->endAngle > *anArc->startAngle + 2 * PI)
+        *anArc->endAngle -= 2.0 * PI;
+    }
+  }
+
+  // update parameters of Middle point constraint for point on arc
+  std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator aCIt = myConstraintMap.begin();
+  for (; aCIt != myConstraintMap.end(); ++aCIt)
+    if (aCIt->second->type() == CONSTRAINT_MIDDLE_POINT) {
+      notify(aCIt->first);
+    }
+}
+
 
 bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
 {
index 3ac8e201e31be774171630d7be1a479b68af647b..107e4205dd89c20ccbbcf367bcf2ea9efce3b071 100644 (file)
@@ -90,6 +90,11 @@ public:
   virtual bool isEmpty() const
   { return SketchSolver_Storage::isEmpty() && myArcConstraintMap.empty(); }
 
+  /// \brief Make parametrization of arcs consistent.
+  ///        Forward arcs should have the last parameter greater than the first parameter.
+  ///        Reversed arcs should have the last parameter lesser than the first parameter.
+  virtual void adjustParametrizationOfArcs();
+
 private:
   /// \brief Convert feature using specified builder.
   EntityWrapperPtr createFeature(const FeaturePtr&             theFeature,
index ac112699d06ab45ca2f90a652d53c559760b17cd..e90def0d2bce33d7fda69f0ee79ce14671aa1386 100644 (file)
@@ -106,7 +106,8 @@ static ConstraintWrapperPtr
                         std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theIntermed);
 static ConstraintWrapperPtr
   createConstraintMiddlePoint(std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
-                              std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity);
+                              std::shared_ptr<PlaneGCSSolver_EdgeWrapper>  theEntity,
+                              std::shared_ptr<PlaneGCSSolver_PointWrapper> theAuxParameters);
 
 static GCS::SET_pD scalarParameters(const ScalarWrapperPtr& theScalar);
 static GCS::SET_pD pointParameters(const PointWrapperPtr& thePoint);
@@ -191,7 +192,7 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
     aResult = createConstraintPointOnEntity(theType, aPoint1, GCS_EDGE_WRAPPER(theEntity1));
     break;
   case CONSTRAINT_MIDDLE_POINT:
-    aResult = createConstraintMiddlePoint(aPoint1, GCS_EDGE_WRAPPER(theEntity1));
+    aResult = createConstraintMiddlePoint(aPoint1, GCS_EDGE_WRAPPER(theEntity1), aPoint2);
     break;
   case CONSTRAINT_PT_PT_DISTANCE:
     aResult = createConstraintDistancePointPoint(GCS_SCALAR_WRAPPER(theValue), aPoint1, aPoint2);
@@ -354,19 +355,38 @@ ConstraintWrapperPtr createConstraintPointOnEntity(
 
 ConstraintWrapperPtr createConstraintMiddlePoint(
     std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
-    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity)
+    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity,
+    std::shared_ptr<PlaneGCSSolver_PointWrapper> theAuxParameters)
 {
+  std::list<GCSConstraintPtr> aConstrList;
+
   GCSPointPtr aPoint = thePoint->point();
   std::shared_ptr<GCS::Line> aLine = std::dynamic_pointer_cast<GCS::Line>(theEntity->entity());
-  if (!aLine)
-    return ConstraintWrapperPtr();
-
-  std::list<GCSConstraintPtr> aConstrList;
-  aConstrList.push_back(
-      GCSConstraintPtr(new GCS::ConstraintPointOnPerpBisector(*aPoint, aLine->p1, aLine->p2)));
-  aConstrList.push_back(GCSConstraintPtr(new GCS::ConstraintPointOnLine(*aPoint, *aLine)));
+  if (aLine) {
+    aConstrList.push_back(GCSConstraintPtr(new GCS::ConstraintPointOnLine(*aPoint, *aLine)));
+    aConstrList.push_back(
+        GCSConstraintPtr(new GCS::ConstraintPointOnPerpBisector(*aPoint, aLine->p1, aLine->p2)));
+  }
+  else {
+    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(theEntity->entity());
+    if (anArc) {
+      double* u = theAuxParameters->point()->x;
+      double* diff = theAuxParameters->point()->y;
+      *u = (*anArc->startAngle + *anArc->endAngle) * 0.5;
+      *diff = (*anArc->endAngle - *anArc->startAngle) * 0.5;
+
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintCurveValue(*aPoint, aPoint->x, *anArc, u)));
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintCurveValue(*aPoint, aPoint->y, *anArc, u)));
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintDifference(anArc->startAngle, u, diff)));
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintDifference(u, anArc->endAngle, diff)));
+    }
+  }
 
-  return ConstraintWrapperPtr(
+  return aConstrList.empty() ? ConstraintWrapperPtr() : ConstraintWrapperPtr(
       new PlaneGCSSolver_ConstraintWrapper(aConstrList, CONSTRAINT_MIDDLE_POINT));
 }
 
@@ -496,7 +516,23 @@ ConstraintWrapperPtr createConstraintPerpendicular(
 {
   std::shared_ptr<GCS::Line> aLine1 = std::dynamic_pointer_cast<GCS::Line>(theEntity1->entity());
   std::shared_ptr<GCS::Line> aLine2 = std::dynamic_pointer_cast<GCS::Line>(theEntity2->entity());
-  GCSConstraintPtr aNewConstr(new GCS::ConstraintPerpendicular(*(aLine1), *(aLine2)));
+
+  std::shared_ptr<GCS::Circle> aCirc1 =
+      std::dynamic_pointer_cast<GCS::Circle>(theEntity1->entity());
+  std::shared_ptr<GCS::Circle> aCirc2 =
+      std::dynamic_pointer_cast<GCS::Circle>(theEntity2->entity());
+
+  GCSConstraintPtr aNewConstr;
+  if (aLine1 && aLine2)
+    aNewConstr.reset(new GCS::ConstraintPerpendicular(*(aLine1), *(aLine2)));
+  else {
+    if (aLine1 && aCirc2)
+      aCirc1 = aCirc2;
+    else if (aLine2 && aCirc1)
+      aLine1 = aLine2;
+
+    aNewConstr.reset(new GCS::ConstraintPointOnLine(aCirc1->center, *aLine1));
+  }
 
   return ConstraintWrapperPtr(
       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_PERPENDICULAR));
index 9643b2aafc7a8f6e180b5ddb81ce2fdb3ec1d075..15b8dc285240300051b3f31c671043bd78a58a5b 100644 (file)
 
 #include <SketchSolver_ConstraintMiddle.h>
 #include <PlaneGCSSolver_ConstraintWrapper.h>
+#include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_Storage.h>
+#include <PlaneGCSSolver_Tools.h>
 #include <PlaneGCSSolver_UpdateCoincidence.h>
 
 void SketchSolver_ConstraintMiddle::getAttributes(
@@ -26,13 +30,55 @@ void SketchSolver_ConstraintMiddle::getAttributes(
     std::vector<EntityWrapperPtr>& theAttributes)
 {
   SketchSolver_Constraint::getAttributes(theValue, theAttributes);
+
+  // create auxiliary point if middle point on arc is specified
+  if (theAttributes[2]->type() == ENTITY_ARC) {
+    std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+        std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+    myOddPoint = GCSPointPtr(new GCS::Point);
+    myOddPoint->x = aStorage->createParameter();
+    myOddPoint->y = aStorage->createParameter();
+    theAttributes[1] = PointWrapperPtr(new PlaneGCSSolver_PointWrapper(myOddPoint));
+  }
+}
+
+bool SketchSolver_ConstraintMiddle::remove()
+{
+  if (myOddPoint) {
+    std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+        std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+    GCS::SET_pD aParams;
+    aParams.insert(myOddPoint->x);
+    aParams.insert(myOddPoint->y);
+    aStorage->removeParameters(aParams);
+  }
+  return SketchSolver_ConstraintCoincidence::remove();
 }
 
 void SketchSolver_ConstraintMiddle::notify(const FeaturePtr&      theFeature,
                                            PlaneGCSSolver_Update* theUpdater)
 {
-  if (theFeature == myBaseConstraint && myInSolver)
-    return; // the constraint is already being updated
+  if (theFeature == myBaseConstraint && myInSolver) {
+    // the constraint is already being updated,
+    // update the middle point parameter if the constraint is "point-on-arc".
+    if (myOddPoint) {
+      EntityWrapperPtr anArcEntity =
+          myAttributes.front()->type() == ENTITY_ARC ? myAttributes.front() : myAttributes.back();
+      EdgeWrapperPtr anArcEdge =
+          std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(anArcEntity);
+      std::shared_ptr<GCS::Arc> anArc;
+      if (anArcEdge)
+        anArc = std::dynamic_pointer_cast<GCS::Arc>(anArcEdge->entity());
+      if (anArc) {
+        // recalculate parameters of middle point according to arc
+        *myOddPoint->x = (*anArc->startAngle + *anArc->endAngle) * 0.5;
+        *myOddPoint->y = (*anArc->endAngle - *anArc->startAngle) * 0.5;
+      }
+    }
+    return;
+  }
 
   PlaneGCSSolver_UpdateCoincidence* anUpdater =
       static_cast<PlaneGCSSolver_UpdateCoincidence*>(theUpdater);
@@ -45,13 +91,8 @@ void SketchSolver_ConstraintMiddle::notify(const FeaturePtr&      theFeature,
         // remove previously adde constraint
         myStorage->removeConstraint(myBaseConstraint);
         // merge divided constraints into single object
-        std::list<GCSConstraintPtr> aGCSConstraints;
-        std::shared_ptr<PlaneGCSSolver_ConstraintWrapper> aConstraint =
-            std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(myMiddle);
-        aGCSConstraints.push_back(aConstraint->constraints().front());
-        aConstraint =
-            std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(mySolverConstraint);
-        aGCSConstraints.push_back(aConstraint->constraints().front());
+        std::list<GCSConstraintPtr> aGCSConstraints = myMiddle->constraints();
+        aGCSConstraints.push_front(mySolverConstraint->constraints().front());
 
         myMiddle = ConstraintWrapperPtr();
         mySolverConstraint = ConstraintWrapperPtr(
@@ -72,10 +113,11 @@ void SketchSolver_ConstraintMiddle::notify(const FeaturePtr&      theFeature,
           std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(mySolverConstraint);
       std::list<GCSConstraintPtr> aGCSConstraints = aConstraint->constraints();
 
-      myMiddle = ConstraintWrapperPtr(
-          new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints.front(), CONSTRAINT_MIDDLE_POINT));
       mySolverConstraint = ConstraintWrapperPtr(
-          new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints.back(), CONSTRAINT_MIDDLE_POINT));
+        new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints.front(), CONSTRAINT_MIDDLE_POINT));
+      aGCSConstraints.pop_front();
+      myMiddle = ConstraintWrapperPtr(
+          new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints, CONSTRAINT_MIDDLE_POINT));
 
       // send middle constraint only
       myStorage->addConstraint(myBaseConstraint, myMiddle);
index 48e11560aebad9cd83b55bda5b00d2d012884fa8..3d63197857411bb8a203aee8876ba95b58bca5ee 100644 (file)
@@ -38,6 +38,9 @@ public:
   virtual void notify(const FeaturePtr&      theFeature,
                       PlaneGCSSolver_Update* theUpdater);
 
+  /// \brief Remove constraint
+  virtual bool remove();
+
 protected:
   /// \brief Generate list of attributes of constraint in order useful for constraints
   /// \param[out] theValue      numerical characteristic of constraint (e.g. distance)
@@ -47,6 +50,7 @@ protected:
 
 private:
   ConstraintWrapperPtr myMiddle;
+  GCSPointPtr myOddPoint; ///< auxiliary point to adjust midpoint-on-arc
 };
 
 #endif
index 5a66f99b006ed8b2f3764f0d53d404b6223737d2..84d701e2b2c10c9b1f7d77566a1c3a64638e5129 100644 (file)
@@ -179,6 +179,7 @@ static SolverConstraintPtr move(StoragePtr theStorage,
     SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
     if (aConstraint->error().empty()) {
       aConstraint->startPoint(theFrom);
+      theStorage->adjustParametrizationOfArcs();
       theSketchSolver->initialize();
       aConstraint->moveTo(theTo);
       theStorage->setNeedToResolve(true);
@@ -236,8 +237,10 @@ bool SketchSolver_Group::resolveConstraints()
 
     PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
     try {
-      if (!isGroupEmpty)
+      if (!isGroupEmpty) {
+        myStorage->adjustParametrizationOfArcs();
         aResult = mySketchSolver->solve();
+      }
       if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
           !myTempConstraints.empty()) {
         mySketchSolver->undo();
index 4d483105843642a9bff4be766c9ff31b0983c3c1..0757abfb7f2abf3f060ff3559a7fec4f09f70831 100644 (file)
@@ -150,6 +150,11 @@ public:
   /// \brief Notify all subscribers about update of the feature
   void notify(const FeaturePtr& theFeature) const;
 
+  /// \brief Make parametrization of arcs consistent.
+  ///        Forward arcs should have the last parameter greater than the first parameter.
+  ///        Reversed arcs should have the last parameter lesser than the first parameter.
+  virtual void adjustParametrizationOfArcs() = 0;
+
 protected:
   /// \brief Convert result to feature or attribute if theResult is linked to center of circle/arc
   static void resultToFeatureOrAttribute(const ObjectPtr& theResult,
index 314c74cb283f94240d26868b9fb39ffceb17e4c2..acd166cb9343a2fa375c219f22e53c6f9605e4c8 100644 (file)
@@ -29,6 +29,7 @@
 #include <GeomAPI_Curve.h>
 #include <GeomAPI_Edge.h>
 #include <GeomAPI_Lin.h>
+#include <GeomAPI_Vertex.h>
 
 
 IMPLEMENT_STANDARD_RTTIEXT(SketcherPrs_Perpendicular, SketcherPrs_SymbolPrs);
@@ -72,22 +73,10 @@ bool SketcherPrs_Perpendicular::updateIfReadyToDisplay(double theStep, bool with
   GeomEdgePtr aEdge1(new GeomAPI_Edge(aShp1));
   GeomEdgePtr aEdge2(new GeomAPI_Edge(aShp2));
 
-  std::shared_ptr<GeomAPI_Lin> aLin1 = aEdge1->line();
-  std::shared_ptr<GeomAPI_Lin> aLin2 = aEdge2->line();
-
-  std::shared_ptr<GeomAPI_Pnt> aPnt = aLin1->intersect(aLin2);
-  double aParam1 = aLin1->projParam(aPnt);
-  double aParam2 = aLin2->projParam(aPnt);
-
-  GeomAPI_Curve aCurve1(aShp1);
-  GeomAPI_Curve aCurve2(aShp2);
-  bool isInside1 = (aParam1 >= (aCurve1.startParam() - Precision::Confusion())) &&
-    (aParam1 <= (aCurve1.endParam() + Precision::Confusion()));
-  bool isInside2 = (aParam2 >= (aCurve2.startParam() - Precision::Confusion())) &&
-    (aParam2 <= (aCurve2.endParam() + Precision::Confusion()));
-
-  if (!(isInside1 && isInside2))
-    aPnt = std::shared_ptr<GeomAPI_Pnt>();
+  GeomShapePtr anInter = aEdge1->intersect(aEdge2);
+  std::shared_ptr<GeomAPI_Pnt> aPnt;
+  if (anInter && anInter->isVertex())
+    aPnt = anInter->vertex()->point();
 
   // Compute position of symbols
   SketcherPrs_PositionMgr* aMgr = SketcherPrs_PositionMgr::get();
@@ -105,14 +94,13 @@ bool SketcherPrs_Perpendicular::updateIfReadyToDisplay(double theStep, bool with
 void SketcherPrs_Perpendicular::drawLines(const Handle(Prs3d_Presentation)& thePrs,
                                           Quantity_Color theColor) const
 {
-  Handle(Graphic3d_Group) aGroup = Prs3d_Root::CurrentGroup(thePrs);
-
-  Handle(Graphic3d_AspectLine3d) aLineAspect =
-    new Graphic3d_AspectLine3d(theColor, Aspect_TOL_SOLID, 2);
-  aGroup->SetPrimitivesAspect(aLineAspect);
-
   // Draw constrained lines
-  addLine(aGroup, SketchPlugin_Constraint::ENTITY_A());
-  addLine(aGroup, SketchPlugin_Constraint::ENTITY_B());
+  for (int i = 0; i < 2; ++i) {
+    ObjectPtr anObj =
+        SketcherPrs_Tools::getResult(myConstraint, SketchPlugin_Constraint::ATTRIBUTE(i));
+    GeomShapePtr aShape = SketcherPrs_Tools::getShape(anObj);
+    if (!aShape)
+      return;
+    drawShape(aShape, thePrs, theColor);
+  }
 }
-
index 226aa30dda252dfedcba4bca6b533df7f10a1a5d..51f9fbec2bfd19da8cf104322a3fb1ca58862315 100644 (file)
@@ -363,6 +363,8 @@ void XGUI_ContextMenuMgr::updateObjectBrowserMenu()
         }
         action("DELETE_CMD")->setEnabled(canBeDeleted);
       }
+      if (hasFeature && myWorkshop->canMoveFeature())
+        action("MOVE_CMD")->setEnabled(true);
     } // end multi-selection
 
     // Check folder management commands state if only features are selected
@@ -784,7 +786,7 @@ void XGUI_ContextMenuMgr::addObjBrowserMenu(QMenu* theMenu) const
       aActions.append(action("ADD_OUT_FOLDER_BEFORE_CMD"));
       aActions.append(action("ADD_OUT_FOLDER_AFTER_CMD"));
       aActions.append(mySeparator3);
-      //aActions.append(action("MOVE_CMD"));
+      aActions.append(action("MOVE_CMD"));
       aActions.append(action("COLOR_CMD"));
       aActions.append(action("DEFLECTION_CMD"));
       aActions.append(action("TRANSPARENCY_CMD"));
index 8b5ccaf65b408673ae09be020965f1dddbbdd80b..4138cc25cdc39eddadbbe8021d49755e7e715dbd 100644 (file)
@@ -207,7 +207,7 @@ void XGUI_PropertyPanel::updateContentWidget(FeaturePtr theFeature)
     eachWidget->restoreValue();
   }
   // the repaint is used here to immediately react in GUI to the values change.
-  repaint();
+  update();
 }
 
 void XGUI_PropertyPanel::createContentPanel(FeaturePtr theFeature)
index df5c48cb13c153a11760d59bac5b59329ef848f6..d5e5bf833fefc51da14c82b6e8e3083e6d626c9c 100644 (file)
@@ -2017,6 +2017,10 @@ void XGUI_Workshop::cleanHistory()
 }
 
 //**************************************************************
+bool compareFeature(const FeaturePtr& theF1, const FeaturePtr& theF2) {
+  DocumentPtr aDoc = theF1->document();
+  return aDoc->index(theF1) < aDoc->index(theF2);
+}
 void XGUI_Workshop::moveObjects()
 {
   if (!abortAllOperations())
@@ -2038,9 +2042,13 @@ void XGUI_Workshop::moveObjects()
   if (!XGUI_Tools::canRemoveOrRename(desktop(), aFeatures))
     return;
 
+  // Sort features by index in document
+  std::list<FeaturePtr> aFList(aFeatures.begin(), aFeatures.end());
+  aFList.sort(compareFeature);
+
   DocumentPtr anActiveDocument = aMgr->activeDocument();
   FeaturePtr aCurrentFeature = anActiveDocument->currentFeature(true);
-  std::set<FeaturePtr>::const_iterator anIt = aFeatures.begin(), aLast = aFeatures.end();
+  std::list<FeaturePtr>::const_iterator anIt = aFList.begin(), aLast = aFList.end();
   for (; anIt != aLast; anIt++) {
     FeaturePtr aFeature = *anIt;
     if (!aFeature.get() || !myModule->canApplyAction(aFeature, anActionId))