]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Task 2.7. Horizontal and Vertical Distance constraint
authorazv <azv@opencascade.com>
Wed, 3 May 2017 05:25:22 +0000 (08:25 +0300)
committerazv <azv@opencascade.com>
Wed, 10 May 2017 12:50:25 +0000 (15:50 +0300)
1. Implemented SketchPlugin entities related to Horizontal and Vertical Distance constraints.
2. Add processing of new constraints in the solver.
3. Special Python commands for new constraints in SketchAPI
4. Unit tests

25 files changed:
src/SketchAPI/SketchAPI_Constraint.cpp
src/SketchAPI/SketchAPI_Rectangle.cpp
src/SketchAPI/SketchAPI_Rectangle.h
src/SketchAPI/SketchAPI_Sketch.cpp
src/SketchAPI/SketchAPI_Sketch.h
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_ConstraintDistance.cpp
src/SketchPlugin/SketchPlugin_ConstraintDistance.h
src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.cpp [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.h [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.cpp [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.h [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_Plugin.cpp
src/SketchPlugin/Test/TestConstraintDistanceBehavior.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintDistanceVertical.py [new file with mode: 0644]
src/SketchPlugin/icons/distance_h.png [new file with mode: 0644]
src/SketchPlugin/icons/distance_v.png [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/SketchSolver_Constraint.cpp
src/SketchSolver/SketchSolver_ConstraintDistance.cpp
src/SketchSolver/SketchSolver_Error.h
src/SketcherPrs/SketcherPrs_LengthDimension.cpp

index 2bf24e02807e2ea9828c9ade47bc7629d3c2025b..e7409f5c225ccc0d383ce8643ba24750d447278f 100644 (file)
@@ -15,6 +15,8 @@
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintCollinear.h>
 #include <SketchPlugin_ConstraintDistance.h>
+#include <SketchPlugin_ConstraintDistanceHorizontal.h>
+#include <SketchPlugin_ConstraintDistanceVertical.h>
 #include <SketchPlugin_ConstraintEqual.h>
 #include <SketchPlugin_ConstraintHorizontal.h>
 #include <SketchPlugin_ConstraintLength.h>
@@ -69,6 +71,14 @@ static const std::string& constraintTypeToSetter(const std::string& theType)
     static const std::string DISTANCE_SETTER("setDistance");
     return DISTANCE_SETTER;
   }
+  if (theType == SketchPlugin_ConstraintDistanceHorizontal::ID()) {
+    static const std::string DISTANCE_SETTER("setHorizontalDistance");
+    return DISTANCE_SETTER;
+  }
+  if (theType == SketchPlugin_ConstraintDistanceVertical::ID()) {
+    static const std::string DISTANCE_SETTER("setVerticalDistance");
+    return DISTANCE_SETTER;
+  }
   if (theType == SketchPlugin_ConstraintEqual::ID()) {
     static const std::string EQUAL_SETTER("setEqual");
     return EQUAL_SETTER;
index ce60335d085650d38339662e9ba06e88c5f0ab18..7337d3b84213900f20ed9a7ea04db39e7550a1f9 100644 (file)
@@ -67,3 +67,12 @@ void SketchAPI_Rectangle::setByPoints(
 
 //--------------------------------------------------------------------------------------
 
+std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_Rectangle::lines() const
+{
+  std::list<FeaturePtr> aFeatures;
+  std::list<ObjectPtr> aList = linesList()->list();
+  std::list<ObjectPtr>::const_iterator anIt = aList.begin();
+  for (; anIt != aList.end(); ++anIt)
+    aFeatures.push_back(ModelAPI_Feature::feature(*anIt));
+  return SketchAPI_SketchEntity::wrap(aFeatures);
+}
index 098d92ff3a26f0dd51b7ce1f817c1a1a3fe06dbf..9853cb34fcb198d21f80a5c857331c5f0aa6b857 100644 (file)
@@ -52,6 +52,9 @@ public:
   SKETCHAPI_EXPORT
   void setByPoints(const std::shared_ptr<GeomAPI_Pnt2d> & theStartPoint,
                    const std::shared_ptr<GeomAPI_Pnt2d> & theEndPoint);
+
+  /// List of lines composing rectangle
+  SKETCHAPI_EXPORT std::list<std::shared_ptr<SketchAPI_SketchEntity> > lines() const;
 };
 
 //! Pointer on Rectangle object
index cc0ec81699027436d963779c7ea8fb2ce71eede3..57704a14647999ee7998473b1a84e24daf653dc1 100644 (file)
@@ -13,6 +13,8 @@
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintCollinear.h>
 #include <SketchPlugin_ConstraintDistance.h>
+#include <SketchPlugin_ConstraintDistanceHorizontal.h>
+#include <SketchPlugin_ConstraintDistanceVertical.h>
 #include <SketchPlugin_ConstraintEqual.h>
 #include <SketchPlugin_Fillet.h>
 #include <SketchPlugin_ConstraintHorizontal.h>
@@ -616,6 +618,34 @@ std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::setDistance(
   return InterfacePtr(new ModelHighAPI_Interface(aFeature));
 }
 
+std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::setHorizontalDistance(
+    const ModelHighAPI_RefAttr & thePoint1,
+    const ModelHighAPI_RefAttr & thePoint2,
+    const ModelHighAPI_Double & theValue)
+{
+  std::shared_ptr<ModelAPI_Feature> aFeature =
+      compositeFeature()->addFeature(SketchPlugin_ConstraintDistanceHorizontal::ID());
+  fillAttribute(thePoint1, aFeature->refattr(SketchPlugin_Constraint::ENTITY_A()));
+  fillAttribute(thePoint2, aFeature->refattr(SketchPlugin_Constraint::ENTITY_B()));
+  fillAttribute(theValue, aFeature->real(SketchPlugin_Constraint::VALUE()));
+  aFeature->execute();
+  return InterfacePtr(new ModelHighAPI_Interface(aFeature));
+}
+
+std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::setVerticalDistance(
+    const ModelHighAPI_RefAttr & thePoint1,
+    const ModelHighAPI_RefAttr & thePoint2,
+    const ModelHighAPI_Double & theValue)
+{
+  std::shared_ptr<ModelAPI_Feature> aFeature =
+      compositeFeature()->addFeature(SketchPlugin_ConstraintDistanceVertical::ID());
+  fillAttribute(thePoint1, aFeature->refattr(SketchPlugin_Constraint::ENTITY_A()));
+  fillAttribute(thePoint2, aFeature->refattr(SketchPlugin_Constraint::ENTITY_B()));
+  fillAttribute(theValue, aFeature->real(SketchPlugin_Constraint::VALUE()));
+  aFeature->execute();
+  return InterfacePtr(new ModelHighAPI_Interface(aFeature));
+}
+
 std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::setEqual(
     const ModelHighAPI_RefAttr & theObject1,
     const ModelHighAPI_RefAttr & theObject2)
index 15450976ff24dfbd705bb12de7b047aff132d70f..e7282dea2300a235616f2adf53518fbfb6f545b5 100644 (file)
@@ -321,6 +321,20 @@ public:
       const ModelHighAPI_RefAttr & thePointOrLine,
       const ModelHighAPI_Double & theValue);
 
+  /// Set horizontal distance
+  SKETCHAPI_EXPORT
+  std::shared_ptr<ModelHighAPI_Interface> setHorizontalDistance(
+      const ModelHighAPI_RefAttr & thePoint1,
+      const ModelHighAPI_RefAttr & thePoint2,
+      const ModelHighAPI_Double & theValue);
+
+  /// Set vertical distance
+  SKETCHAPI_EXPORT
+  std::shared_ptr<ModelHighAPI_Interface> setVerticalDistance(
+      const ModelHighAPI_RefAttr & thePoint1,
+      const ModelHighAPI_RefAttr & thePoint2,
+      const ModelHighAPI_Double & theValue);
+
   /// Set equal
   SKETCHAPI_EXPORT
   std::shared_ptr<ModelHighAPI_Interface> setEqual(
index 010ee47f9e4ac46899f98fee3416bca0497b2cf4..e1358181263709732c16191b89122e111d12ba84 100644 (file)
@@ -13,6 +13,8 @@ SET(PROJECT_HEADERS
     SketchPlugin_ConstraintCoincidence.h
     SketchPlugin_ConstraintCollinear.h
     SketchPlugin_ConstraintDistance.h
+    SketchPlugin_ConstraintDistanceHorizontal.h
+    SketchPlugin_ConstraintDistanceVertical.h
     SketchPlugin_ConstraintEqual.h
     SketchPlugin_ConstraintHorizontal.h
     SketchPlugin_ConstraintLength.h
@@ -56,6 +58,8 @@ SET(PROJECT_SOURCES
     SketchPlugin_ConstraintCoincidence.cpp
     SketchPlugin_ConstraintCollinear.cpp
     SketchPlugin_ConstraintDistance.cpp
+    SketchPlugin_ConstraintDistanceHorizontal.cpp
+    SketchPlugin_ConstraintDistanceVertical.cpp
     SketchPlugin_ConstraintEqual.cpp
     SketchPlugin_ConstraintHorizontal.cpp
     SketchPlugin_ConstraintLength.cpp
@@ -141,6 +145,9 @@ ADD_UNIT_TESTS(TestSketchPointLine.py
                TestConstraintCollinear.py
                TestConstraintLength.py
                TestConstraintDistance.py
+               TestConstraintDistanceHorizontal.py
+               TestConstraintDistanceVertical.py
+               TestConstraintDistanceBehavior.py
                TestConstraintParallel.py
                TestConstraintPerpendicular.py
                TestConstraintRadius.py
index eef61d0a900e7802f6842308c7c96b7b2df725da..6f7627c45beedfdd0ba478191f9b29e5b7ee5ebd 100644 (file)
@@ -164,6 +164,32 @@ double SketchPlugin_ConstraintDistance::calculateCurrentDistance()
   return aDistance;
 }
 
+bool SketchPlugin_ConstraintDistance::areAttributesInitialized()
+{
+  std::shared_ptr<ModelAPI_Data> aData = data();
+  std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
+  std::shared_ptr<GeomDataAPI_Point2D> aPointA =
+      SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_A(), aPlane);
+  std::shared_ptr<GeomDataAPI_Point2D> aPointB =
+      SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane);
+
+  if (!aPointA && !aPointB)
+    return false;
+  else if (aPointA || aPointB) {
+    FeaturePtr aLine;
+    if (!aPointA)
+      aLine = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
+    else if (!aPointB)
+      aLine = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
+    else // both points are initialized
+      return true;
+
+    if (!aLine || aLine->getKind() != SketchPlugin_Line::ID())
+      return false;
+  }
+  return true;
+}
+
 void SketchPlugin_ConstraintDistance::attributeChanged(const std::string& theID)
 {
   if (theID == SketchPlugin_Constraint::ENTITY_A() ||
index ef9e3eb1a506e6eaaa98a944130b628fcb7f9c7d..37cbe6f7252183303c944c0525eb6e6facedd191 100644 (file)
@@ -71,7 +71,10 @@ class SketchPlugin_ConstraintDistance : public SketchPlugin_ConstraintBase
 
 protected:
   /// Returns the current distance between the feature attributes
-  double calculateCurrentDistance();
+  virtual double calculateCurrentDistance();
+
+  /// Check the attributes related to distanced points/features are initialized
+  bool areAttributesInitialized();
 
 private:
   bool myFlyoutUpdate; ///< to avoid cyclic dependencies on automatic updates of flyout point
diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.cpp b/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.cpp
new file mode 100644 (file)
index 0000000..29c8ce2
--- /dev/null
@@ -0,0 +1,149 @@
+// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
+
+// File:    SketchPlugin_ConstraintDistanceHorizontal.cpp
+// Created: 2 May 2017
+// Author:  Artem ZHIDKOV
+
+#include <SketchPlugin_ConstraintDistanceHorizontal.h>
+
+#include <SketcherPrs_Tools.h>
+#include <SketcherPrs_Factory.h>
+
+#include <GeomAPI_Dir2d.h>
+#include <GeomAPI_XY.h>
+#include <GeomDataAPI_Point2D.h>
+
+#include <ModelAPI_AttributeDouble.h>
+
+const double tolerance = 1e-7;
+
+
+SketchPlugin_ConstraintDistanceHorizontal::SketchPlugin_ConstraintDistanceHorizontal()
+  : SketchPlugin_ConstraintDistance()
+{
+}
+
+//*************************************************************************************
+void SketchPlugin_ConstraintDistanceHorizontal::initAttributes()
+{
+  data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
+}
+
+//*************************************************************************************
+void SketchPlugin_ConstraintDistanceHorizontal::execute()
+{
+  AttributeDoublePtr anAttrValue = real(SketchPlugin_Constraint::VALUE());
+  if (anAttrValue->isInitialized() || !areAttributesInitialized())
+    return;
+
+  double aDistance = calculateCurrentDistance();
+  anAttrValue->setValue(aDistance);
+}
+
+//*************************************************************************************
+AISObjectPtr SketchPlugin_ConstraintDistanceHorizontal::getAISObject(AISObjectPtr thePrevious)
+{
+  if (!sketch())
+    return thePrevious;
+
+  AISObjectPtr anAIS = SketcherPrs_Factory::lengthDimensionConstraint(this,
+                                                                      sketch()->coordinatePlane(),
+                                                                      thePrevious);
+  return anAIS;
+}
+
+//*************************************************************************************
+void SketchPlugin_ConstraintDistanceHorizontal::move(double theDeltaX, double theDeltaY)
+{
+  std::shared_ptr<ModelAPI_Data> aData = data();
+  if (!aData->isValid())
+    return;
+
+  // Recalculate a shift of flyout point in terms of local coordinates
+  std::shared_ptr<GeomAPI_XY> aDir(new GeomAPI_XY(theDeltaX, theDeltaY));
+  std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
+  std::shared_ptr<GeomDataAPI_Point2D> aPointA = SketcherPrs_Tools::getFeaturePoint(
+      data(), SketchPlugin_Constraint::ENTITY_A(), aPlane);
+  std::shared_ptr<GeomDataAPI_Point2D> aPointB = SketcherPrs_Tools::getFeaturePoint(
+      data(), SketchPlugin_Constraint::ENTITY_B(), aPlane);
+
+  if (!aPointA || !aPointB)
+    return;
+
+  std::shared_ptr<GeomAPI_XY> aStartPnt = aPointA->pnt()->xy();
+  std::shared_ptr<GeomAPI_XY> aEndPnt = aPointB->pnt()->xy();
+
+  std::shared_ptr<GeomAPI_Dir2d> aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt)));
+  double dX = aDir->dot(aLineDir->xy());
+  double dY = -aDir->cross(aLineDir->xy());
+
+  std::shared_ptr<GeomDataAPI_Point2D> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
+  myFlyoutUpdate = true;
+  if (aPoint->isInitialized()) {
+    aPoint->setValue(aPoint->x() + dX, aPoint->y() + dY);
+  } else {
+    aPoint->setValue(dX, dY);
+  }
+  myFlyoutUpdate = false;
+}
+
+double SketchPlugin_ConstraintDistanceHorizontal::calculateCurrentDistance()
+{
+  std::shared_ptr<ModelAPI_Data> aData = data();
+  std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
+  std::shared_ptr<GeomDataAPI_Point2D> aPointA =
+    SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_A(), aPlane);
+  std::shared_ptr<GeomDataAPI_Point2D> aPointB =
+      SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane);
+
+  return aPointB->x() - aPointA->x();
+}
+
+void SketchPlugin_ConstraintDistanceHorizontal::attributeChanged(const std::string& theID)
+{
+  if (theID == SketchPlugin_Constraint::ENTITY_A() ||
+      theID == SketchPlugin_Constraint::ENTITY_B())
+  {
+    AttributeDoublePtr aValueAttr = real(SketchPlugin_Constraint::VALUE());
+    if (!aValueAttr->isInitialized() && areAttributesInitialized()) {
+      // only if it is not initialized, try to compute the current value
+      double aDistance = calculateCurrentDistance();
+      aValueAttr->setValue(aDistance);
+    }
+  } else if (theID == SketchPlugin_Constraint::FLYOUT_VALUE_PNT() && !myFlyoutUpdate) {
+    myFlyoutUpdate = true;
+    // Recalculate flyout point in local coordinates of the distance constraint:
+    // the X coordinate is a length of projection of the flyout point on the
+    //                  line binding two distanced points
+    //                  or a line of projection of the distanced point onto the distanced segment
+    // the Y coordinate is a distance from the flyout point to the line
+    std::shared_ptr<GeomDataAPI_Point2D> aFlyoutAttr =
+        std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
+    std::shared_ptr<GeomAPI_Pnt2d> aFlyoutPnt = aFlyoutAttr->pnt();
+
+    std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
+    std::shared_ptr<GeomDataAPI_Point2D> aPointA = SketcherPrs_Tools::getFeaturePoint(
+        data(), SketchPlugin_Constraint::ENTITY_A(), aPlane);
+    std::shared_ptr<GeomDataAPI_Point2D> aPointB = SketcherPrs_Tools::getFeaturePoint(
+        data(), SketchPlugin_Constraint::ENTITY_B(), aPlane);
+
+    std::shared_ptr<GeomAPI_XY> aStartPnt = aPointA->pnt()->xy();
+    std::shared_ptr<GeomAPI_XY> aEndPnt = aPointB->pnt()->xy();
+
+    if (aEndPnt->distance(aStartPnt) < tolerance)
+      return;
+
+    std::shared_ptr<GeomAPI_Dir2d> aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt)));
+    std::shared_ptr<GeomAPI_XY> aFlyoutDir = aFlyoutPnt->xy()->decreased(aStartPnt);
+
+    double X = aFlyoutDir->dot(aLineDir->xy());
+    double Y = -aFlyoutDir->cross(aLineDir->xy());
+    aFlyoutAttr->setValue(X, Y);
+    myFlyoutUpdate = false;
+  }
+}
diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.h b/src/SketchPlugin/SketchPlugin_ConstraintDistanceHorizontal.h
new file mode 100644 (file)
index 0000000..948fe13
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
+
+// File:    SketchPlugin_ConstraintDistanceHorizontal.h
+// Created: 2 May 2017
+// Author:  Artem ZHIDKOV
+
+#ifndef SketchPlugin_ConstraintDistanceHorizontal_H_
+#define SketchPlugin_ConstraintDistanceHorizontal_H_
+
+#include <SketchPlugin.h>
+#include <SketchPlugin_ConstraintDistance.h>
+
+/** \class SketchPlugin_ConstraintDistanceHorizontal
+ *  \ingroup Plugins
+ *  \brief Feature for creation of a new constraint which defines a horizontal distance between two points.
+ *
+ *  This constraint has three attributes:
+ *  SketchPlugin_Constraint::VALUE(), SketchPlugin_Constraint::ENTITY_A() and SketchPlugin_Constraint::ENTITY_B()
+ */
+class SketchPlugin_ConstraintDistanceHorizontal : public SketchPlugin_ConstraintDistance
+{
+public:
+  /// Distance constraint kind
+  inline static const std::string& ID()
+  {
+    static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistanceHorizontal");
+    return MY_CONSTRAINT_DISTANCE_ID;
+  }
+
+  /// \brief Returns the kind of a feature
+  SKETCHPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = SketchPlugin_ConstraintDistanceHorizontal::ID();
+    return MY_KIND;
+  }
+
+  /// \brief Creates a new part document if needed
+  SKETCHPLUGIN_EXPORT virtual void execute();
+
+  /// \brief Request for initialization of data model of the feature: adding all attributes
+  SKETCHPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Returns the AIS preview
+  SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious);
+
+  /// Moves the feature
+  /// \param theDeltaX the delta for X coordinate is moved
+  /// \param theDeltaY the delta for Y coordinate is moved
+  SKETCHPLUGIN_EXPORT virtual void move(const double theDeltaX, const double theDeltaY);
+
+  /// Called on change of any argument-attribute of this object
+  /// \param theID identifier of changed attribute
+  SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
+
+  /// \brief Use plugin manager for features creation
+  SketchPlugin_ConstraintDistanceHorizontal();
+
+protected:
+  /// Returns the current distance between the feature attributes
+  virtual double calculateCurrentDistance();
+
+private:
+  bool myFlyoutUpdate; ///< to avoid cyclic dependencies on automatic updates of flyout point
+};
+
+#endif
diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.cpp b/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.cpp
new file mode 100644 (file)
index 0000000..49db91f
--- /dev/null
@@ -0,0 +1,149 @@
+// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
+
+// File:    SketchPlugin_ConstraintDistanceVertical.cpp
+// Created: 10 May 2017
+// Author:  Artem ZHIDKOV
+
+#include <SketchPlugin_ConstraintDistanceVertical.h>
+
+#include <SketcherPrs_Tools.h>
+#include <SketcherPrs_Factory.h>
+
+#include <GeomAPI_Dir2d.h>
+#include <GeomAPI_XY.h>
+#include <GeomDataAPI_Point2D.h>
+
+#include <ModelAPI_AttributeDouble.h>
+
+const double tolerance = 1e-7;
+
+
+SketchPlugin_ConstraintDistanceVertical::SketchPlugin_ConstraintDistanceVertical()
+  : SketchPlugin_ConstraintDistance()
+{
+}
+
+//*************************************************************************************
+void SketchPlugin_ConstraintDistanceVertical::initAttributes()
+{
+  data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
+  data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
+}
+
+//*************************************************************************************
+void SketchPlugin_ConstraintDistanceVertical::execute()
+{
+  AttributeDoublePtr anAttrValue = real(SketchPlugin_Constraint::VALUE());
+  if (anAttrValue->isInitialized() || !areAttributesInitialized())
+    return;
+
+  double aDistance = calculateCurrentDistance();
+  anAttrValue->setValue(aDistance);
+}
+
+//*************************************************************************************
+AISObjectPtr SketchPlugin_ConstraintDistanceVertical::getAISObject(AISObjectPtr thePrevious)
+{
+  if (!sketch())
+    return thePrevious;
+
+  AISObjectPtr anAIS = SketcherPrs_Factory::lengthDimensionConstraint(this,
+                                                                      sketch()->coordinatePlane(),
+                                                                      thePrevious);
+  return anAIS;
+}
+
+//*************************************************************************************
+void SketchPlugin_ConstraintDistanceVertical::move(double theDeltaX, double theDeltaY)
+{
+  std::shared_ptr<ModelAPI_Data> aData = data();
+  if (!aData->isValid())
+    return;
+
+  // Recalculate a shift of flyout point in terms of local coordinates
+  std::shared_ptr<GeomAPI_XY> aDir(new GeomAPI_XY(theDeltaX, theDeltaY));
+  std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
+  std::shared_ptr<GeomDataAPI_Point2D> aPointA = SketcherPrs_Tools::getFeaturePoint(
+      data(), SketchPlugin_Constraint::ENTITY_A(), aPlane);
+  std::shared_ptr<GeomDataAPI_Point2D> aPointB = SketcherPrs_Tools::getFeaturePoint(
+      data(), SketchPlugin_Constraint::ENTITY_B(), aPlane);
+
+  if (!aPointA || !aPointB)
+    return;
+
+  std::shared_ptr<GeomAPI_XY> aStartPnt = aPointA->pnt()->xy();
+  std::shared_ptr<GeomAPI_XY> aEndPnt = aPointB->pnt()->xy();
+
+  std::shared_ptr<GeomAPI_Dir2d> aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt)));
+  double dX = aDir->dot(aLineDir->xy());
+  double dY = -aDir->cross(aLineDir->xy());
+
+  std::shared_ptr<GeomDataAPI_Point2D> aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aData->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
+  myFlyoutUpdate = true;
+  if (aPoint->isInitialized()) {
+    aPoint->setValue(aPoint->x() + dX, aPoint->y() + dY);
+  } else {
+    aPoint->setValue(dX, dY);
+  }
+  myFlyoutUpdate = false;
+}
+
+double SketchPlugin_ConstraintDistanceVertical::calculateCurrentDistance()
+{
+  std::shared_ptr<ModelAPI_Data> aData = data();
+  std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
+  std::shared_ptr<GeomDataAPI_Point2D> aPointA =
+    SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_A(), aPlane);
+  std::shared_ptr<GeomDataAPI_Point2D> aPointB =
+      SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane);
+
+  return aPointB->y() - aPointA->y();
+}
+
+void SketchPlugin_ConstraintDistanceVertical::attributeChanged(const std::string& theID)
+{
+  if (theID == SketchPlugin_Constraint::ENTITY_A() ||
+      theID == SketchPlugin_Constraint::ENTITY_B())
+  {
+    AttributeDoublePtr aValueAttr = real(SketchPlugin_Constraint::VALUE());
+    if (!aValueAttr->isInitialized() && areAttributesInitialized()) {
+      // only if it is not initialized, try to compute the current value
+      double aDistance = calculateCurrentDistance();
+      aValueAttr->setValue(aDistance);
+    }
+  } else if (theID == SketchPlugin_Constraint::FLYOUT_VALUE_PNT() && !myFlyoutUpdate) {
+    myFlyoutUpdate = true;
+    // Recalculate flyout point in local coordinates of the distance constraint:
+    // the X coordinate is a length of projection of the flyout point on the
+    //                  line binding two distanced points
+    //                  or a line of projection of the distanced point onto the distanced segment
+    // the Y coordinate is a distance from the flyout point to the line
+    std::shared_ptr<GeomDataAPI_Point2D> aFlyoutAttr =
+        std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()));
+    std::shared_ptr<GeomAPI_Pnt2d> aFlyoutPnt = aFlyoutAttr->pnt();
+
+    std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
+    std::shared_ptr<GeomDataAPI_Point2D> aPointA = SketcherPrs_Tools::getFeaturePoint(
+        data(), SketchPlugin_Constraint::ENTITY_A(), aPlane);
+    std::shared_ptr<GeomDataAPI_Point2D> aPointB = SketcherPrs_Tools::getFeaturePoint(
+        data(), SketchPlugin_Constraint::ENTITY_B(), aPlane);
+
+    std::shared_ptr<GeomAPI_XY> aStartPnt = aPointA->pnt()->xy();
+    std::shared_ptr<GeomAPI_XY> aEndPnt = aPointB->pnt()->xy();
+
+    if (aEndPnt->distance(aStartPnt) < tolerance)
+      return;
+
+    std::shared_ptr<GeomAPI_Dir2d> aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt)));
+    std::shared_ptr<GeomAPI_XY> aFlyoutDir = aFlyoutPnt->xy()->decreased(aStartPnt);
+
+    double X = aFlyoutDir->dot(aLineDir->xy());
+    double Y = -aFlyoutDir->cross(aLineDir->xy());
+    aFlyoutAttr->setValue(X, Y);
+    myFlyoutUpdate = false;
+  }
+}
diff --git a/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.h b/src/SketchPlugin/SketchPlugin_ConstraintDistanceVertical.h
new file mode 100644 (file)
index 0000000..e5f14d8
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
+
+// File:    SketchPlugin_ConstraintDistanceVertical.h
+// Created: 10 May 2017
+// Author:  Artem ZHIDKOV
+
+#ifndef SketchPlugin_ConstraintDistanceVertical_H_
+#define SketchPlugin_ConstraintDistanceVertical_H_
+
+#include <SketchPlugin.h>
+#include <SketchPlugin_ConstraintDistance.h>
+
+/** \class SketchPlugin_ConstraintDistanceVertical
+ *  \ingroup Plugins
+ *  \brief Feature for creation of a new constraint which defines a vertical distance between two points.
+ *
+ *  This constraint has three attributes:
+ *  SketchPlugin_Constraint::VALUE(), SketchPlugin_Constraint::ENTITY_A() and SketchPlugin_Constraint::ENTITY_B()
+ */
+class SketchPlugin_ConstraintDistanceVertical : public SketchPlugin_ConstraintDistance
+{
+public:
+  /// Distance constraint kind
+  inline static const std::string& ID()
+  {
+    static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistanceVertical");
+    return MY_CONSTRAINT_DISTANCE_ID;
+  }
+
+  /// \brief Returns the kind of a feature
+  SKETCHPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = SketchPlugin_ConstraintDistanceVertical::ID();
+    return MY_KIND;
+  }
+
+  /// \brief Creates a new part document if needed
+  SKETCHPLUGIN_EXPORT virtual void execute();
+
+  /// \brief Request for initialization of data model of the feature: adding all attributes
+  SKETCHPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Returns the AIS preview
+  SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious);
+
+  /// Moves the feature
+  /// \param theDeltaX the delta for X coordinate is moved
+  /// \param theDeltaY the delta for Y coordinate is moved
+  SKETCHPLUGIN_EXPORT virtual void move(const double theDeltaX, const double theDeltaY);
+
+  /// Called on change of any argument-attribute of this object
+  /// \param theID identifier of changed attribute
+  SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
+
+  /// \brief Use plugin manager for features creation
+  SketchPlugin_ConstraintDistanceVertical();
+
+protected:
+  /// Returns the current distance between the feature attributes
+  virtual double calculateCurrentDistance();
+
+private:
+  bool myFlyoutUpdate; ///< to avoid cyclic dependencies on automatic updates of flyout point
+};
+
+#endif
index 9999ea08c7ca04c7fe3788709e047191f3c9b151..4776e5e3a4ba3a39b146080ededa566871adec98 100644 (file)
@@ -12,6 +12,8 @@
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintCollinear.h>
 #include <SketchPlugin_ConstraintDistance.h>
+#include <SketchPlugin_ConstraintDistanceHorizontal.h>
+#include <SketchPlugin_ConstraintDistanceVertical.h>
 #include <SketchPlugin_ConstraintEqual.h>
 #include <SketchPlugin_Fillet.h>
 #include <SketchPlugin_ConstraintSplit.h>
@@ -172,6 +174,10 @@ FeaturePtr SketchPlugin_Plugin::createFeature(std::string theFeatureID)
     return FeaturePtr(new SketchPlugin_ConstraintCollinear);
   } else if (theFeatureID == SketchPlugin_ConstraintDistance::ID()) {
     return FeaturePtr(new SketchPlugin_ConstraintDistance);
+  } else if (theFeatureID == SketchPlugin_ConstraintDistanceHorizontal::ID()) {
+    return FeaturePtr(new SketchPlugin_ConstraintDistanceHorizontal);
+  } else if (theFeatureID == SketchPlugin_ConstraintDistanceVertical::ID()) {
+    return FeaturePtr(new SketchPlugin_ConstraintDistanceVertical);
   } else if (theFeatureID == SketchPlugin_ConstraintLength::ID()) {
     return FeaturePtr(new SketchPlugin_ConstraintLength);
   } else if (theFeatureID == SketchPlugin_ConstraintParallel::ID()) {
@@ -256,6 +262,7 @@ std::shared_ptr<ModelAPI_FeatureStateMessage> SketchPlugin_Plugin
       aMsg->setState(SketchPlugin_Line::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Circle::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Arc::ID(), aHasSketchPlane);
+      aMsg->setState(SketchPlugin_Ellipse::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Projection::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_ConstraintCoincidence::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_ConstraintCollinear::ID(), aHasSketchPlane);
@@ -279,6 +286,8 @@ std::shared_ptr<ModelAPI_FeatureStateMessage> SketchPlugin_Plugin
       aMsg->setState(SketchPlugin_Trim::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MacroArc::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MacroCircle::ID(), aHasSketchPlane);
+      aMsg->setState(SketchPlugin_ConstraintDistanceHorizontal::ID(), aHasSketchPlane);
+      aMsg->setState(SketchPlugin_ConstraintDistanceVertical::ID(), aHasSketchPlane);
       // SketchRectangle is a python feature, so its ID is passed just as a string
       aMsg->setState("SketchRectangle", aHasSketchPlane);
     }
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceBehavior.py b/src/SketchPlugin/Test/TestConstraintDistanceBehavior.py
new file mode 100644 (file)
index 0000000..37c0458
--- /dev/null
@@ -0,0 +1,82 @@
+from salome.shaper import model
+from SketchAPI import *
+import math
+
+TOLERANCE = 1.e-7
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+DistanceParam = model.addParameter(Part_1_doc, "distance", "10.")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchRectangle_1 = Sketch_1.addRectangle(20., 20., 70., 50.)
+[SketchLine_1, SketchLine_2, SketchLine_3, SketchLine_4] = SketchRectangle_1.lines()
+firstPoint = SketchAPI_Line(SketchLine_2).startPoint()
+secondPoint = SketchAPI_Line(SketchLine_3).endPoint()
+model.do()
+
+# =============================================================================
+# Test 1.
+# =============================================================================
+# horizontal distance constraint
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(firstPoint, secondPoint, "distance")
+model.do()
+
+# changing the parameter
+for param in range(-30, 31, 2):
+    DistanceParam.setValue(param)
+    model.do()
+    dist = secondPoint.x() - firstPoint.x()
+    assert math.fabs(dist - param) < TOLERANCE, "Incorrect horizontal distance {}, expected {}".format(dist, param)
+
+model.testNbSubFeatures(Sketch_1, "SketchLine", 4)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintDistanceHorizontal", 1)
+
+# remove horizontal distance constraint
+Part_1_doc.removeFeature(SketchConstraintDistanceHorizontal_1.feature())
+model.do()
+
+# =============================================================================
+# Test 2.
+# =============================================================================
+# Vertical distance constraint
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(firstPoint, secondPoint, "distance")
+model.do()
+
+# changing the parameter
+for param in range(-30, 31, 2):
+    DistanceParam.setValue(param)
+    model.do()
+    dist = secondPoint.y() - firstPoint.y()
+    assert math.fabs(dist - param) < TOLERANCE, "Incorrect vertical distance {}, expected {}".format(dist, param)
+
+model.testNbSubFeatures(Sketch_1, "SketchLine", 4)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintDistanceVertical", 1)
+
+# remove verticel distance constraint
+Part_1_doc.removeFeature(SketchConstraintDistanceVertical_1.feature())
+model.do()
+
+# =============================================================================
+# Test 3.
+# =============================================================================
+# distance constraint
+SketchConstraintDistance_1 = Sketch_1.setDistance(firstPoint, secondPoint, "distance")
+model.do()
+
+# changing the parameter
+for param in range(-30, 31, 2):
+    DistanceParam.setValue(param)
+    model.do()
+    if param <= 0:
+        assert SketchConstraintDistance_1.feature().error() != '', "ERROR: Sketch should not be valid due to negative distance value"
+    else:
+        dist = model.distancePointPoint(firstPoint, secondPoint)
+        assert math.fabs(dist - math.fabs(param)) < TOLERANCE, "Incorrect distance {}, expected {}".format(dist, math.fabs(param))
+
+model.testNbSubFeatures(Sketch_1, "SketchLine", 4)
+model.testNbSubFeatures(Sketch_1, "SketchConstraintDistance", 1)
+# leave distance constraint alive
+
+model.end()
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py b/src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py
new file mode 100644 (file)
index 0000000..dedb8d6
--- /dev/null
@@ -0,0 +1,208 @@
+"""
+    TestConstraintDistanceHorizontal.py
+    Unit test of SketchPlugin_ConstraintDistanceHorizontal class
+
+    SketchPlugin_ConstraintDistanceHorizontal
+        static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistance");
+        data()->addAttribute(SketchPlugin_Constraint::VALUE(),    ModelAPI_AttributeDouble::typeId());
+        data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId());
+        data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
+        data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
+"""
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2017-05-10"
+
+
+def horizontalDistance(point1, point2):
+    """
+    subroutine to calculate signed distance between two points
+    """
+    return point2.x() - point1.x()
+
+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()
+#=========================================================================
+# Create two movable and one fixed point
+#=========================================================================
+aSession.startOperation()
+aPoint1 = aSketchFeature.addFeature("SketchPoint")
+aPoint1Coords = geomDataAPI_Point2D(aPoint1.attribute("PointCoordinates"))
+aPoint1Coords.setValue(50., 50.)
+aSession.finishOperation()
+aSession.startOperation()
+aPoint2 = aSketchFeature.addFeature("SketchPoint")
+aPoint2Coords = geomDataAPI_Point2D(aPoint2.attribute("PointCoordinates"))
+aPoint2Coords.setValue(70., 70.)
+aSession.finishOperation()
+aSession.startOperation()
+anOriginResult = modelAPI_Result(aDocument.objectByName("Construction", "Origin"))
+anOriginShape = anOriginResult.shape()
+anExtPoint = aSketchFeature.addFeature("SketchPoint")
+anExtCoords = geomDataAPI_Point2D(anExtPoint.attribute("PointCoordinates"))
+anExtCoords.setValue(0., 0.)
+anExtPoint.selection("External").setValue(anOriginResult, anOriginShape)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 4)
+
+#=========================================================================
+# Create a constraint to keep horizontal distance between movable points
+#=========================================================================
+DISTANCE1 = 25.
+aSession.startOperation()
+aHDist1 = aSketchFeature.addFeature("SketchConstraintDistanceHorizontal")
+aDistance = aHDist1.real("ConstraintValue")
+refattrA = aHDist1.refattr("ConstraintEntityA")
+refattrB = aHDist1.refattr("ConstraintEntityB")
+assert (not aDistance.isInitialized())
+assert (not refattrA.isInitialized())
+assert (not refattrB.isInitialized())
+refattrA.setObject(aPoint1.lastResult())
+refattrB.setObject(aPoint2.lastResult())
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 3)
+# set flyout point then abort operation, after that check the Distance is correct
+aSession.startOperation()
+aFlyoutPoint = geomDataAPI_Point2D(aHDist1.attribute("ConstraintFlyoutValuePnt"))
+aFlyoutPoint.setValue(50.0, 100.0)
+aSession.abortOperation()
+assert (refattrA.isInitialized())
+assert (refattrB.isInitialized())
+assert (aDistance.isInitialized())
+aSession.startOperation()
+aDistance.setValue(DISTANCE1)
+aSession.finishOperation()
+assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+assert (model.dof(aSketchFeature) == 3)
+#=========================================================================
+# Change a distance value
+#=========================================================================
+d = DISTANCE1 + 20.
+dStep = -5.
+while d >= -30.:
+    aSession.startOperation()
+    DISTANCE1 = d
+    aDistance.setValue(DISTANCE1)
+    aSession.finishOperation()
+    assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    d += dStep
+assert (model.dof(aSketchFeature) == 3)
+
+#=========================================================================
+# Create a constraint to keep horizontal distance between fixed and movable points
+#=========================================================================
+DISTANCE2 = 50.
+aSession.startOperation()
+aHDist2 = aSketchFeature.addFeature("SketchConstraintDistanceHorizontal")
+aDistance = aHDist2.real("ConstraintValue")
+refattrA = aHDist2.refattr("ConstraintEntityA")
+refattrB = aHDist2.refattr("ConstraintEntityB")
+assert (not aDistance.isInitialized())
+assert (not refattrA.isInitialized())
+assert (not refattrB.isInitialized())
+refattrA.setObject(anExtPoint.lastResult())
+refattrB.setAttr(aPoint1Coords)
+aDistance.setValue(DISTANCE2)
+aSession.finishOperation()
+assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+assert (model.dof(aSketchFeature) == 2)
+#=========================================================================
+# Change a distance value (check previous constraint is applied too)
+#=========================================================================
+d = DISTANCE2
+dStep = -5.
+while d >= -50.:
+    aSession.startOperation()
+    DISTANCE2 = d
+    aDistance.setValue(DISTANCE2)
+    aSession.finishOperation()
+    assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+    assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+    assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    d += dStep
+assert (model.dof(aSketchFeature) == 2)
+
+#=========================================================================
+# Remove first distance
+#=========================================================================
+aStoredCoords = [aPoint2Coords.x(), aPoint2Coords.y()]
+aSession.startOperation()
+aDocument.removeFeature(aHDist1)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 3)
+aSession.startOperation()
+DISTANCE2 = 20.
+aDistance.setValue(DISTANCE2)
+aSession.finishOperation()
+assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+assert aPoint2Coords.x() == aStoredCoords[0] and aPoint2Coords.y() == aStoredCoords[1]
+
+#=========================================================================
+# Create line and set horizontal distance between line boundaries
+#=========================================================================
+aSession.startOperation()
+aLine = aSketchFeature.addFeature("SketchLine")
+aStartPoint = geomDataAPI_Point2D(aLine.attribute("StartPoint"))
+aEndPoint = geomDataAPI_Point2D(aLine.attribute("EndPoint"))
+aStartPoint.setValue(50., 0.)
+aEndPoint.setValue(100., 20.)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 7)
+
+DISTANCE3 = 50.
+aSession.startOperation()
+aHDist3 = aSketchFeature.addFeature("SketchConstraintDistanceHorizontal")
+aDistance = aHDist3.real("ConstraintValue")
+refattrA = aHDist3.refattr("ConstraintEntityA")
+refattrB = aHDist3.refattr("ConstraintEntityB")
+assert (not aDistance.isInitialized())
+assert (not refattrA.isInitialized())
+assert (not refattrB.isInitialized())
+refattrA.setAttr(aStartPoint)
+refattrB.setAttr(aEndPoint)
+aDistance.setValue(DISTANCE3)
+aSession.finishOperation()
+assert math.fabs(horizontalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aStartPoint, aEndPoint), DISTANCE3)
+assert (model.dof(aSketchFeature) == 6)
+#=========================================================================
+# Change a distance value
+#=========================================================================
+d = DISTANCE3
+dStep = -5.
+while d >= -50.:
+    aSession.startOperation()
+    DISTANCE3 = d
+    aDistance.setValue(DISTANCE3)
+    aSession.finishOperation()
+    assert math.fabs(horizontalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aStartPoint, aEndPoint), DISTANCE3)
+    d += dStep
+assert (model.dof(aSketchFeature) == 6)
+
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceVertical.py b/src/SketchPlugin/Test/TestConstraintDistanceVertical.py
new file mode 100644 (file)
index 0000000..09ba211
--- /dev/null
@@ -0,0 +1,208 @@
+"""
+    TestConstraintDistanceVertical.py
+    Unit test of SketchPlugin_ConstraintDistanceVertical class
+
+    SketchPlugin_ConstraintDistanceVertical
+        static const std::string MY_CONSTRAINT_DISTANCE_ID("SketchConstraintDistance");
+        data()->addAttribute(SketchPlugin_Constraint::VALUE(),    ModelAPI_AttributeDouble::typeId());
+        data()->addAttribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT(), GeomDataAPI_Point2D::typeId());
+        data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
+        data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
+"""
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2017-05-10"
+
+
+def verticalDistance(point1, point2):
+    """
+    subroutine to calculate signed distance between two points
+    """
+    return point2.y() - point1.y()
+
+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()
+#=========================================================================
+# Create two movable and one fixed point
+#=========================================================================
+aSession.startOperation()
+aPoint1 = aSketchFeature.addFeature("SketchPoint")
+aPoint1Coords = geomDataAPI_Point2D(aPoint1.attribute("PointCoordinates"))
+aPoint1Coords.setValue(50., 50.)
+aSession.finishOperation()
+aSession.startOperation()
+aPoint2 = aSketchFeature.addFeature("SketchPoint")
+aPoint2Coords = geomDataAPI_Point2D(aPoint2.attribute("PointCoordinates"))
+aPoint2Coords.setValue(70., 70.)
+aSession.finishOperation()
+aSession.startOperation()
+anOriginResult = modelAPI_Result(aDocument.objectByName("Construction", "Origin"))
+anOriginShape = anOriginResult.shape()
+anExtPoint = aSketchFeature.addFeature("SketchPoint")
+anExtCoords = geomDataAPI_Point2D(anExtPoint.attribute("PointCoordinates"))
+anExtCoords.setValue(0., 0.)
+anExtPoint.selection("External").setValue(anOriginResult, anOriginShape)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 4)
+
+#=========================================================================
+# Create a constraint to keep vertical distance between movable points
+#=========================================================================
+DISTANCE1 = 25.
+aSession.startOperation()
+aVDist1 = aSketchFeature.addFeature("SketchConstraintDistanceVertical")
+aDistance = aVDist1.real("ConstraintValue")
+refattrA = aVDist1.refattr("ConstraintEntityA")
+refattrB = aVDist1.refattr("ConstraintEntityB")
+assert (not aDistance.isInitialized())
+assert (not refattrA.isInitialized())
+assert (not refattrB.isInitialized())
+refattrA.setObject(aPoint1.lastResult())
+refattrB.setObject(aPoint2.lastResult())
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 3)
+# set flyout point then abort operation, after that check the Distance is correct
+aSession.startOperation()
+aFlyoutPoint = geomDataAPI_Point2D(aVDist1.attribute("ConstraintFlyoutValuePnt"))
+aFlyoutPoint.setValue(50.0, 100.0)
+aSession.abortOperation()
+assert (refattrA.isInitialized())
+assert (refattrB.isInitialized())
+assert (aDistance.isInitialized())
+aSession.startOperation()
+aDistance.setValue(DISTANCE1)
+aSession.finishOperation()
+assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+assert (model.dof(aSketchFeature) == 3)
+#=========================================================================
+# Change a distance value
+#=========================================================================
+d = DISTANCE1 + 20.
+dStep = -5.
+while d >= -30.:
+    aSession.startOperation()
+    DISTANCE1 = d
+    aDistance.setValue(DISTANCE1)
+    aSession.finishOperation()
+    assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    d += dStep
+assert (model.dof(aSketchFeature) == 3)
+
+#=========================================================================
+# Create a constraint to keep vertical distance between fixed and movable points
+#=========================================================================
+DISTANCE2 = 50.
+aSession.startOperation()
+aVDist2 = aSketchFeature.addFeature("SketchConstraintDistanceVertical")
+aDistance = aVDist2.real("ConstraintValue")
+refattrA = aVDist2.refattr("ConstraintEntityA")
+refattrB = aVDist2.refattr("ConstraintEntityB")
+assert (not aDistance.isInitialized())
+assert (not refattrA.isInitialized())
+assert (not refattrB.isInitialized())
+refattrA.setObject(anExtPoint.lastResult())
+refattrB.setAttr(aPoint1Coords)
+aDistance.setValue(DISTANCE2)
+aSession.finishOperation()
+assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+assert (model.dof(aSketchFeature) == 2)
+#=========================================================================
+# Change a distance value (check previous constraint is applied too)
+#=========================================================================
+d = DISTANCE2
+dStep = -5.
+while d >= -50.:
+    aSession.startOperation()
+    DISTANCE2 = d
+    aDistance.setValue(DISTANCE2)
+    aSession.finishOperation()
+    assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+    assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+    assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    d += dStep
+assert (model.dof(aSketchFeature) == 2)
+
+#=========================================================================
+# Remove first distance
+#=========================================================================
+aStoredCoords = [aPoint2Coords.x(), aPoint2Coords.y()]
+aSession.startOperation()
+aDocument.removeFeature(aVDist1)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 3)
+aSession.startOperation()
+DISTANCE2 = 20.
+aDistance.setValue(DISTANCE2)
+aSession.finishOperation()
+assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+assert aPoint2Coords.x() == aStoredCoords[0] and aPoint2Coords.y() == aStoredCoords[1]
+
+#=========================================================================
+# Create line and set vertical distance between line boundaries
+#=========================================================================
+aSession.startOperation()
+aLine = aSketchFeature.addFeature("SketchLine")
+aStartPoint = geomDataAPI_Point2D(aLine.attribute("StartPoint"))
+aEndPoint = geomDataAPI_Point2D(aLine.attribute("EndPoint"))
+aStartPoint.setValue(50., 0.)
+aEndPoint.setValue(100., 20.)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 7)
+
+DISTANCE3 = 50.
+aSession.startOperation()
+aVDist3 = aSketchFeature.addFeature("SketchConstraintDistanceVertical")
+aDistance = aVDist3.real("ConstraintValue")
+refattrA = aVDist3.refattr("ConstraintEntityA")
+refattrB = aVDist3.refattr("ConstraintEntityB")
+assert (not aDistance.isInitialized())
+assert (not refattrA.isInitialized())
+assert (not refattrB.isInitialized())
+refattrA.setAttr(aStartPoint)
+refattrB.setAttr(aEndPoint)
+aDistance.setValue(DISTANCE3)
+aSession.finishOperation()
+assert math.fabs(verticalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aStartPoint, aEndPoint), DISTANCE3)
+assert (model.dof(aSketchFeature) == 6)
+#=========================================================================
+# Change a distance value
+#=========================================================================
+d = DISTANCE3
+dStep = -5.
+while d >= -50.:
+    aSession.startOperation()
+    DISTANCE3 = d
+    aDistance.setValue(DISTANCE3)
+    aSession.finishOperation()
+    assert math.fabs(verticalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aStartPoint, aEndPoint), DISTANCE3)
+    d += dStep
+assert (model.dof(aSketchFeature) == 6)
+
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/icons/distance_h.png b/src/SketchPlugin/icons/distance_h.png
new file mode 100644 (file)
index 0000000..b88db21
Binary files /dev/null and b/src/SketchPlugin/icons/distance_h.png differ
diff --git a/src/SketchPlugin/icons/distance_v.png b/src/SketchPlugin/icons/distance_v.png
new file mode 100644 (file)
index 0000000..027a384
Binary files /dev/null and b/src/SketchPlugin/icons/distance_v.png differ
index e33f3dede8309a80a9095bfaaa38f0ee984f3d84..10673347829e1d5d7a97de5f17d4b3e3411fdb7a 100644 (file)
@@ -10,7 +10,7 @@
                 SketchEllipse SketchMacroEllipse
                 SketchRectangle
                 SketchProjection
-                SketchConstraintLength SketchConstraintRadius SketchConstraintDistance
+                SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintDistanceHorizontal SketchConstraintDistanceVertical
                 SketchConstraintParallel SketchConstraintPerpendicular
                 SketchConstraintRigid SketchConstraintHorizontal SketchConstraintVertical
                 SketchConstraintEqual SketchConstraintTangent
         <validator id="PartSet_DistanceSelection"/>
       </feature>
 
-    <!--  SketchConstraintLength  -->
+      <!--  SketchConstraintDistanceHorizontal  -->
+      <feature
+        id="SketchConstraintDistanceHorizontal"
+        title="Horizontal Distance"
+        tooltip="Set horizontal distance between two points"
+        icon="icons/Sketch/distance_h.png">
+        <label title="Select points for distance definition."/>
+        <sketch_shape_selector
+              id="ConstraintEntityA"
+              label="First point"
+              tooltip="Select point."
+              shape_types="vertex">
+          <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityB"/>
+          <validator id="PartSet_DifferentObjects"/>
+          <validator id="GeomValidators_ShapeType" parameters="vertex"/>
+        </sketch_shape_selector>
+        <sketch_shape_selector
+          id="ConstraintEntityB"
+          label="Second point"
+          tooltip="Select point."
+          shape_types="vertex">
+          <validator id="PartSet_DifferentObjects"/>
+          <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityA"/>
+          <validator id="GeomValidators_ShapeType" parameters="vertex"/>
+        </sketch_shape_selector>
+        <sketch-2dpoint_flyout_selector id="ConstraintFlyoutValuePnt"  default="computed" internal="1" obligatory="0"/>
+
+        <doublevalue_editor label="Value" tooltip="Distance" id="ConstraintValue" default="computed"/>
+
+        <validator id="PartSet_DistanceSelection"/>
+      </feature>
+
+      <!--  SketchConstraintDistanceVertical  -->
+      <feature
+        id="SketchConstraintDistanceVertical"
+        title="Vertical Distance"
+        tooltip="Set vertical distance between two points"
+        icon="icons/Sketch/distance_v.png">
+        <label title="Select points for distance definition."/>
+        <sketch_shape_selector
+              id="ConstraintEntityA"
+              label="First point"
+              tooltip="Select point."
+              shape_types="vertex">
+          <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityB"/>
+          <validator id="PartSet_DifferentObjects"/>
+          <validator id="GeomValidators_ShapeType" parameters="vertex"/>
+        </sketch_shape_selector>
+        <sketch_shape_selector
+          id="ConstraintEntityB"
+          label="Second point"
+          tooltip="Select point."
+          shape_types="vertex">
+          <validator id="PartSet_DifferentObjects"/>
+          <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityA"/>
+          <validator id="GeomValidators_ShapeType" parameters="vertex"/>
+        </sketch_shape_selector>
+        <sketch-2dpoint_flyout_selector id="ConstraintFlyoutValuePnt"  default="computed" internal="1" obligatory="0"/>
+
+        <doublevalue_editor label="Value" tooltip="Distance" id="ConstraintValue" default="computed"/>
+
+        <validator id="PartSet_DistanceSelection"/>
+      </feature>
+
+      <!--  SketchConstraintLength  -->
       <feature id="SketchConstraintLength" title="Length" tooltip="Set fixed length of a line segment" icon="icons/Sketch/length.png">
         <label title="Select a line on which to calculate length" tooltip="Select a line on which to calculate length"/>
         <shape_selector id="ConstraintEntityA" label="Line" tooltip="Select a line" shape_types="edge" >
index 3fa64daa86238c460ee6613324decccdff610b1a..156ace951510cabc73b42d014c567e4a5b085d7d 100644 (file)
@@ -50,6 +50,8 @@ enum SketchSolver_ConstraintType {
   CONSTRAINT_DISTANCE,         // base distance if we don't know the measured objects yet
   CONSTRAINT_PT_PT_DISTANCE,
   CONSTRAINT_PT_LINE_DISTANCE,
+  CONSTRAINT_HORIZONTAL_DISTANCE,
+  CONSTRAINT_VERTICAL_DISTANCE,
   CONSTRAINT_RADIUS,
   CONSTRAINT_ANGLE,
   CONSTRAINT_FIXED,
index fb7e70919a11b043e246468789bb2979ff0d0002..09aa35dffa579be29f0a8a01677b468772ebce49 100644 (file)
@@ -28,6 +28,8 @@
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintCollinear.h>
 #include <SketchPlugin_ConstraintDistance.h>
+#include <SketchPlugin_ConstraintDistanceHorizontal.h>
+#include <SketchPlugin_ConstraintDistanceVertical.h>
 #include <SketchPlugin_ConstraintEqual.h>
 #include <SketchPlugin_ConstraintLength.h>
 #include <SketchPlugin_ConstraintMiddle.h>
@@ -62,6 +64,11 @@ static ConstraintWrapperPtr
   createConstraintDistancePointLine(std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue,
                                     std::shared_ptr<PlaneGCSSolver_PointWrapper>  thePoint,
                                     std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity);
+static ConstraintWrapperPtr
+  createConstraintHVDistance(const SketchSolver_ConstraintType& theType,
+                             std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue,
+                             std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint1,
+                             std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint2);
 static ConstraintWrapperPtr
   createConstraintRadius(std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue,
                          std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity);
@@ -98,7 +105,9 @@ SolverConstraintPtr PlaneGCSSolver_Tools::createConstraint(ConstraintPtr theCons
     return SolverConstraintPtr(new SketchSolver_ConstraintCoincidence(theConstraint));
   } else if (theConstraint->getKind() == SketchPlugin_ConstraintCollinear::ID()) {
     return SolverConstraintPtr(new SketchSolver_ConstraintCollinear(theConstraint));
-  } else if (theConstraint->getKind() == SketchPlugin_ConstraintDistance::ID()) {
+  } else if (theConstraint->getKind() == SketchPlugin_ConstraintDistance::ID() ||
+             theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID() ||
+             theConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID()) {
     return SolverConstraintPtr(new SketchSolver_ConstraintDistance(theConstraint));
   } else if (theConstraint->getKind() == SketchPlugin_ConstraintEqual::ID()) {
     return SolverConstraintPtr(new SketchSolver_ConstraintEqual(theConstraint));
@@ -173,6 +182,10 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
                                                 aPoint1,
                                                 GCS_EDGE_WRAPPER(theEntity1));
     break;
+  case CONSTRAINT_HORIZONTAL_DISTANCE:
+  case CONSTRAINT_VERTICAL_DISTANCE:
+    aResult = createConstraintHVDistance(theType, GCS_SCALAR_WRAPPER(theValue), aPoint1, aPoint2);
+    break;
   case CONSTRAINT_RADIUS:
     aResult = createConstraintRadius(GCS_SCALAR_WRAPPER(theValue),
                                      GCS_EDGE_WRAPPER(theEntity1));
@@ -345,6 +358,32 @@ ConstraintWrapperPtr createConstraintDistancePointLine(
   return aResult;
 }
 
+ConstraintWrapperPtr createConstraintHVDistance(
+    const SketchSolver_ConstraintType& theType,
+    std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue,
+    std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint1,
+    std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint2)
+{
+  GCSPointPtr aPoint1 = thePoint1->point();
+  GCSPointPtr aPoint2 = thePoint2->point();
+
+  double *aParam1, *aParam2;
+  if (theType == CONSTRAINT_HORIZONTAL_DISTANCE) {
+    aParam1 = aPoint1->x;
+    aParam2 = aPoint2->x;
+  } else if (theType == CONSTRAINT_VERTICAL_DISTANCE) {
+    aParam1 = aPoint1->y;
+    aParam2 = aPoint2->y;
+  }
+
+  GCSConstraintPtr aNewConstr(new GCS::ConstraintDifference(aParam1, aParam2, theValue->scalar()));
+
+  std::shared_ptr<PlaneGCSSolver_ConstraintWrapper> aResult(
+      new PlaneGCSSolver_ConstraintWrapper(aNewConstr, theType));
+  aResult->setValueParameter(theValue);
+  return aResult;
+}
+
 ConstraintWrapperPtr createConstraintRadius(
     std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue,
     std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity)
index 1a4a594849be4b01941bc3d333eee3ed13fb5bb3..5c73c9a9bd517f2c45a87faf2bdaac46a3c2d0ea 100644 (file)
@@ -116,6 +116,10 @@ void SketchSolver_Constraint::process()
   ConstraintWrapperPtr aNewConstraint = PlaneGCSSolver_Tools::createConstraint(
       myBaseConstraint, aConstrType,
       aValue, anAttributes[0], anAttributes[1], anAttributes[2], anAttributes[3]);
+  if (!aNewConstraint) {
+    myErrorMsg = SketchSolver_Error::WRONG_CONSTRAINT_TYPE();
+    return;
+  }
   myStorage->addConstraint(myBaseConstraint, aNewConstraint);
 
   adjustConstraint();
index 9e01c65a8377ae06fb3db06751e8f1da238aa7cd..dc8c029b882dcf6cba28bdab67e63f7193cddc39 100644 (file)
@@ -4,6 +4,9 @@
 #include <SketchSolver_Error.h>
 #include <SketchSolver_Manager.h>
 
+#include <SketchPlugin_ConstraintDistanceHorizontal.h>
+#include <SketchPlugin_ConstraintDistanceVertical.h>
+
 #include <GeomAPI_Dir2d.h>
 #include <GeomAPI_Lin2d.h>
 #include <GeomAPI_Pnt2d.h>
@@ -22,9 +25,14 @@ void SketchSolver_ConstraintDistance::getAttributes(
     return;
   }
 
-  if (theAttributes[1])
-    myType = CONSTRAINT_PT_PT_DISTANCE;
-  else if (theAttributes[2] && theAttributes[2]->type() == ENTITY_LINE)
+  if (theAttributes[1]) {
+    if (myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID())
+      myType = CONSTRAINT_HORIZONTAL_DISTANCE;
+    else if (myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID())
+      myType = CONSTRAINT_VERTICAL_DISTANCE;
+    else
+      myType = CONSTRAINT_PT_PT_DISTANCE;
+  } else if (theAttributes[2] && theAttributes[2]->type() == ENTITY_LINE)
     myType = CONSTRAINT_PT_LINE_DISTANCE;
   else
     theAttributes.clear();
index a4fe899451b0339d61c03287085b45402d278cb1..9a57e38a5a89308009166bf22c8f7a48394d9b5d 100644 (file)
@@ -89,6 +89,12 @@ class SketchSolver_Error
     static const std::string MY_ERROR_VALUE("Caution: SolveSpace crash! Constraints are wrong");
     return MY_ERROR_VALUE;
   }
+  /// Constraint has wrong type
+  inline static const std::string& WRONG_CONSTRAINT_TYPE()
+  {
+    static const std::string MY_ERROR_VALUE("Unsupported type of constraint");
+    return MY_ERROR_VALUE;
+  }
 };
 
 #endif
index 300d30c7fd9045a91852f59ed0693bca87716e58..af9c69d9e08ac0d9d185998da08b7639dba7fa37 100644 (file)
@@ -11,6 +11,8 @@
 #include <SketchPlugin_Constraint.h>
 #include <SketchPlugin_ConstraintLength.h>
 #include <SketchPlugin_ConstraintDistance.h>
+#include <SketchPlugin_ConstraintDistanceHorizontal.h>
+#include <SketchPlugin_ConstraintDistanceVertical.h>
 #include <SketchPlugin_Line.h>
 #include <SketchPlugin_Point.h>
 #include <SketchPlugin_Circle.h>
@@ -169,7 +171,9 @@ bool SketcherPrs_LengthDimension::readyToDisplay(ModelAPI_Feature* theConstraint
     thePnt2 = thePlane->to3D(aEndPoint->x(), aEndPoint->y())->impl<gp_Pnt>();
     return true;
 
-  } else if (theConstraint->getKind() == SketchPlugin_ConstraintDistance::ID()) {
+  } else if (theConstraint->getKind() == SketchPlugin_ConstraintDistance::ID() ||
+             theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID() ||
+             theConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID()) {
     // The constraint is distance
     std::shared_ptr<GeomDataAPI_Point2D> aPoint_A = SketcherPrs_Tools::getFeaturePoint(
         aData, SketchPlugin_Constraint::ENTITY_A(), thePlane);