Salome HOME
Sketcher: implement possibility to set zero distance
authorazv <azv@opencascade.com>
Tue, 22 Oct 2019 13:04:13 +0000 (16:04 +0300)
committerazv <azv@opencascade.com>
Tue, 22 Oct 2019 13:04:13 +0000 (16:04 +0300)
18 files changed:
src/GeomData/GeomData_Dir.cpp
src/ModelHighAPI/ModelHighAPI_FeatureStore.cpp
src/ModuleBase/ModuleBase_Tools.cpp
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_ConstraintDistance.cpp
src/SketchPlugin/SketchPlugin_ConstraintDistance.h
src/SketchPlugin/SketchPlugin_Line.cpp
src/SketchPlugin/SketchPlugin_Line.h
src/SketchPlugin/Test/TestConstraintDistanceBehavior.py
src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py
src/SketchPlugin/Test/TestConstraintDistanceHorizontalZero.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintDistanceVertical.py
src/SketchPlugin/Test/TestConstraintDistanceVerticalZero.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintDistanceZero.py [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ConstraintWrapper.cpp
src/SketchSolver/SketchSolver_ConstraintDistance.cpp
src/SketcherPrs/SketcherPrs_LengthDimension.cpp

index d03030e359c845a8f83594f45ff13137bde423ec..23c7ce1c4141c879640569ab080848dd5b811f93 100644 (file)
@@ -79,5 +79,6 @@ void GeomData_Dir::reinit()
   if (!myIsInitialized) {
     // create attribute: not initialized by value yet, just zero
     myCoords = TDataStd_RealArray::Set(myLab, 0, 2);
+    myIsInitialized = true;
   }
 }
index fc73000c04b2fb1753ed3a6e2dc68ef128244d02..2cf757443be12442821253fe138cfb1d0e9b11a4 100644 (file)
@@ -368,6 +368,8 @@ std::string ModelHighAPI_FeatureStore::dumpAttr(const AttributePtr& theAttr) {
     double aValues[3] = {anAttr->x(), anAttr->y(), anAttr->z()};
     dumpArray(aResult, aValues, 3);
   } else if (aType == GeomDataAPI_Dir::typeId()) {
+    if (theAttr->id() == "DistanceDirection")
+      return "__notcase__";
     AttributeDirPtr anAttr = std::dynamic_pointer_cast<GeomDataAPI_Dir>(theAttr);
     double aValues[3] = {anAttr->x(), anAttr->y(), anAttr->z()};
     dumpArray(aResult, aValues, 3);
index 2d6e806705cf5f35ceb4808ecab2c8dfc2fa6528..bf36a09f056f9803cc61afc13d563214d8b7bcef 100644 (file)
@@ -256,7 +256,7 @@ void setSpinValue(QDoubleSpinBox* theSpin, double theValue)
 
 void setSpinValue(ModuleBase_ParamSpinBox* theSpin, double theValue)
 {
-  if (fabs(theSpin->value() - theValue) < tolerance)
+  if (!theSpin->text().isEmpty() && fabs(theSpin->value() - theValue) < tolerance)
     return;
   bool isBlocked = theSpin->blockSignals(true);
   theSpin->setValue(theValue);
index d32b708caa6481d26549c66ec3a3b34e1c824351..051f3fab29e51cb7afda3db9e43b5b4d52063631 100644 (file)
@@ -219,6 +219,9 @@ ADD_UNIT_TESTS(
   TestConstraintDistanceBehavior.py
   TestConstraintDistanceHorizontal.py
   TestConstraintDistanceVertical.py
+  TestConstraintDistanceZero.py
+  TestConstraintDistanceHorizontalZero.py
+  TestConstraintDistanceVerticalZero.py
   TestConstraintEqual.py
   TestConstraintEqualEllipse.py
   TestConstraintFixed.py
index 16a0451d4044c1987e970bf4b56adea306ed4e8a..47399b4166ec0df751f4b5436179fb83392bc5a4 100644 (file)
@@ -30,6 +30,7 @@
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_XY.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Dir.h>
 
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_AttributeInteger.h>
@@ -61,6 +62,11 @@ void SketchPlugin_ConstraintDistance::initAttributes()
   data()->addAttribute(SketchPlugin_ConstraintDistance::LOCATION_TYPE_ID(),
                        ModelAPI_AttributeInteger::typeId());
   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), LOCATION_TYPE_ID());
+
+  AttributePtr anAttr = data()->addAttribute(SketchPlugin_ConstraintDistance::DIRECTION_ID(),
+                                             GeomDataAPI_Dir::typeId());
+  anAttr->setIsArgument(false);
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), DIRECTION_ID());
 }
 
 void SketchPlugin_ConstraintDistance::colorConfigInfo(std::string& theSection, std::string& theName,
@@ -98,6 +104,20 @@ AISObjectPtr SketchPlugin_ConstraintDistance::getAISObject(AISObjectPtr thePrevi
   return anAIS;
 }
 
+static std::shared_ptr<GeomAPI_Lin2d> getLine(DataPtr theData, const std::string& theAttrName)
+{
+  FeaturePtr aLineFeature = SketcherPrs_Tools::getFeatureLine(theData, theAttrName);
+  if (!aLineFeature)
+    return GeomLine2dPtr();
+
+  std::shared_ptr<GeomDataAPI_Point2D> aStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::START_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::END_ID()));
+
+  return GeomLine2dPtr(new GeomAPI_Lin2d(aStart->x(), aStart->y(), aEnd->x(), aEnd->y()));
+}
+
 double SketchPlugin_ConstraintDistance::calculateCurrentDistance()
 {
   double aDistance = -1.;
@@ -109,25 +129,39 @@ double SketchPlugin_ConstraintDistance::calculateCurrentDistance()
   std::shared_ptr<GeomDataAPI_Point2D> aPointB =
       SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane);
 
+  GeomPnt2dPtr aGeomPntA, aGeomPntB;
+  GeomLine2dPtr aLine;
   if (aPointA.get() && aPointB.get()) {  // both points
-    aDistance = aPointA->pnt()->distance(aPointB->pnt());
+    aGeomPntA = aPointA->pnt();
+    aGeomPntB = aPointB->pnt();
   } else {
-    FeaturePtr aLineFeature;
-    std::shared_ptr<SketchPlugin_Line> aLine;
     if (!aPointA.get() && aPointB.get()) {  //Line and point
-      aLineFeature = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
-      aLine = std::dynamic_pointer_cast<SketchPlugin_Line>(aLineFeature);
-      if (aLine.get()) {
-        aDistance = aLine->distanceToPoint(aPointB->pnt());
-      }
+      aLine = getLine(aData, SketchPlugin_Constraint::ENTITY_A());
+      aGeomPntB = aPointB->pnt();
+      aGeomPntA = aLine ? aLine->project(aGeomPntB) : GeomPnt2dPtr();
     } else if (aPointA.get() && !aPointB.get()) {  // Point and line
-      aLineFeature = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
-      aLine = std::dynamic_pointer_cast<SketchPlugin_Line>(aLineFeature);
-      if (aLine.get()) {
-        aDistance = aLine->distanceToPoint(aPointA->pnt());
-      }
+      aLine = getLine(aData, SketchPlugin_Constraint::ENTITY_B());
+      aGeomPntA = aPointA->pnt();
+      aGeomPntB = aLine ? aLine->project(aGeomPntA) : GeomPnt2dPtr();
     }
   }
+
+  if (aGeomPntA.get() && aGeomPntB.get()) {
+    aDistance = aGeomPntA->distance(aGeomPntB);
+    if (aDistance < tolerance)
+      aDistance = 0.0;
+  }
+
+  // keep the direction between arguments for processing of the zero distance
+  std::shared_ptr<GeomDataAPI_Dir> aPointPointDir =
+      std::dynamic_pointer_cast<GeomDataAPI_Dir>(attribute(DIRECTION_ID()));
+  if (aDistance > tolerance)
+    aPointPointDir->setValue(aGeomPntB->x() - aGeomPntA->x(), aGeomPntB->y() - aGeomPntA->y(), 0.0);
+  else if (aLine) {
+    GeomDir2dPtr aLineDir = aLine->direction();
+    aPointPointDir->setValue(-aLineDir->y(), aLineDir->x(), 0.0);
+  }
+
   return aDistance;
 }
 
@@ -167,7 +201,7 @@ void SketchPlugin_ConstraintDistance::attributeChanged(const std::string& theID)
     if (!aValueAttr->isInitialized()) {
       // only if it is not initialized, try to compute the current value
       double aDistance = calculateCurrentDistance();
-      if (aDistance > 0) { // set as value the length of updated references
+      if (aDistance >= 0) { // set as value the length of updated references
         aValueAttr->setValue(aDistance);
       }
     }
index 082fb84a897927910399878373ec109bfec7a2f5..9ce5afbe79d47bed7d1c0cabb612d7b0b7ed77e5 100644 (file)
@@ -64,6 +64,14 @@ class SketchPlugin_ConstraintDistance : public SketchPlugin_ConstraintBase
     return MY_SIGNED;
   }
 
+  /// \brief The direction from the first object to the second.
+  ///        To change distance value from zero to non-zero correctly.
+  inline static const std::string& DIRECTION_ID()
+  {
+    static const std::string MY_DIRECTION_ID("DistanceDirection");
+    return MY_DIRECTION_ID;
+  }
+
   /// attribute name of dimension location type
   inline static const std::string& LOCATION_TYPE_ID()
   {
index f01a332f296b0b90d751165547938b937376df3f..3e9e9fd394b413d15268d2706b820be04b9ec53e 100644 (file)
@@ -104,28 +104,6 @@ std::string SketchPlugin_Line::processEvent(const std::shared_ptr<Events_Message
 }
 // LCOV_EXCL_STOP
 
-double SketchPlugin_Line::distanceToPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
-{
-  double aDelta = 0;
-
-  std::shared_ptr<ModelAPI_Data> aData = data();
-  std::shared_ptr<GeomDataAPI_Point2D> aPoint1 =
-    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aData->attribute(START_ID()));
-  std::shared_ptr<GeomDataAPI_Point2D> aPoint2 =
-    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aData->attribute(END_ID()));
-
-  GeomAPI_Lin2d aLin2d(aPoint1->x(), aPoint1->y(), aPoint2->x(), aPoint2->y());
-
-  if (false/*projection*/) {  // TODO: if it has not been necessary, remove this block
-    std::shared_ptr<GeomAPI_Pnt2d> aResult = aLin2d.project(thePoint);
-    aDelta = aResult->distance(thePoint);
-  } else {  // distance
-    aDelta = aLin2d.distance(thePoint);
-  }
-
-  return aDelta;
-}
-
 const std::string& SketchPlugin_Line::getKind()
 {
   static std::string MY_KIND = SketchPlugin_Line::ID();
index 7a23944ae6260d0ab8714a83579e07ea033e931a..28d60dcd0ccab75233db146247cbfc8c9cc1a963 100644 (file)
@@ -80,10 +80,6 @@ class SketchPlugin_Line : public SketchPlugin_SketchEntity,
   /// if message has selected object
   virtual std::string processEvent(const std::shared_ptr<Events_Message>& theMessage);
 
-  /// Return the distance between the feature and the point
-  /// \param thePoint the point
-  double distanceToPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
-
   /// Called on change of any argument-attribute of this object
   SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
 
index 413331d7238e016769e7f86fc8a4af98e61818b4..f230ce1d4ade5583923676cac0376bb2371a206f 100644 (file)
@@ -44,7 +44,7 @@ SketchConstraintDistanceHorizontal_1.feature().real("ConstraintValue").setText(D
 model.do()
 
 # changing the parameter
-for param in range(-30, 31, 2):
+for param in range(-31, 30, 2):
     if param == 0:
         continue
     DistanceParam.setValue(param)
@@ -68,7 +68,7 @@ SketchConstraintDistanceVertical_1.feature().real("ConstraintValue").setText(Dis
 model.do()
 
 # changing the parameter
-for param in range(-30, 31, 2):
+for param in range(-31, 30, 2):
     if param == 0:
         continue
     DistanceParam.setValue(param)
@@ -94,8 +94,10 @@ model.do()
 for param in range(-30, 31, 2):
     DistanceParam.setValue(param)
     model.do()
-    if param <= 0:
+    if param < 0:
         assert SketchConstraintDistance_1.feature().error() != '', "ERROR: Sketch should not be valid due to negative distance value"
+    elif param == 0: # constraint is valid, but lead to degenerated geometry
+        assert Sketch_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))
index 1a6bcbfa1d035154aa5e25004750f27f565873ce..155f4d79eaa8ee6a76086042770caf461acfee96 100644 (file)
@@ -117,17 +117,14 @@ assert (model.dof(aSketchFeature) == 3)
 #=========================================================================
 # Change a distance value
 #=========================================================================
-d = DISTANCE1 + 20.
+d = DISTANCE1 + 21.
 dStep = -5.
 while d >= -30.:
     aSession.startOperation()
     DISTANCE1 = d
     aDistance.setValue(DISTANCE1)
     aSession.finishOperation()
-    if DISTANCE1 == 0:
-        assert(aHDist1.error() != "")
-    else:
-        assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    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)
 
@@ -154,18 +151,15 @@ assert (model.dof(aSketchFeature) == 2)
 # Change a distance value (check previous constraint is applied too)
 #=========================================================================
 d = DISTANCE2
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE2 = d
     aDistance.setValue(DISTANCE2)
     aSession.finishOperation()
-    if DISTANCE2 == 0:
-        assert(aHDist2.error() != "")
-    else:
-        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)
+    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)
 
@@ -216,16 +210,13 @@ assert (model.dof(aSketchFeature) == 6)
 # Change a distance value
 #=========================================================================
 d = DISTANCE3
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE3 = d
     aDistance.setValue(DISTANCE3)
     aSession.finishOperation()
-    if DISTANCE3 == 0:
-        assert(aHDist3.error() != "")
-    else:
-        assert math.fabs(horizontalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aStartPoint, aEndPoint), DISTANCE3)
+    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)
 
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceHorizontalZero.py b/src/SketchPlugin/Test/TestConstraintDistanceHorizontalZero.py
new file mode 100644 (file)
index 0000000..cccb7b9
--- /dev/null
@@ -0,0 +1,122 @@
+# 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
+#
+
+"""
+    Test the zero value of the constraint "DistanceHorizontal"
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from SketchAPI import *
+
+__updated__ = "2019-10-22"
+TOLERANCE = 1.e-6
+
+class TestZeroDistanceHorizontal(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myLine1 = self.mySketch.addLine(10, 10, 45, 27.5)
+    self.myLine2 = self.mySketch.addLine(20, 15, 30, 40)
+    self.myLine3 = self.mySketch.addLine(10, 0, 10, 10)
+    model.do()
+    self.myDOF = 12
+
+  def tearDown(self):
+    model.end()
+    model.checkSketch(self.mySketch, self.myDOF)
+
+  def assertDistanceHorizontal(self, theObject1, theObject2, theDistance):
+    dist = theObject2.x() - theObject1.x()
+    self.assertTrue(math.fabs(dist - theDistance) < TOLERANCE, "Current distance = {}, reference = {}".format(dist, theDistance))
+    model.checkSketch(self.mySketch, self.myDOF)
+
+
+  def test_distance_positive_nzznz(self):
+    """ Test 1. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine2.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+
+  def test_distance_negative_nzznz(self):
+    """ Test 2. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 15)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.endPoint(), self.myLine2.endPoint(), -15)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.endPoint(), self.myLine2.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.endPoint(), self.myLine2.endPoint(), -10)
+
+  def test_distance_equal_znzz(self):
+    """ Test 3. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.endPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+  def test_distance_notequal_znzz(self):
+    """ Test 4. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.startPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
index b4365147f67b06b0567650fafd1d9b46466b2b4f..8c0b1b6c384ed24730540c88a0dcf6d1b28e1a68 100644 (file)
@@ -117,17 +117,14 @@ assert (model.dof(aSketchFeature) == 3)
 #=========================================================================
 # Change a distance value
 #=========================================================================
-d = DISTANCE1 + 20.
+d = DISTANCE1 + 21.
 dStep = -5.
 while d >= -30.:
     aSession.startOperation()
     DISTANCE1 = d
     aDistance.setValue(DISTANCE1)
     aSession.finishOperation()
-    if DISTANCE1 == 0:
-        assert(aVDist1.error() != "")
-    else:
-        assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    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)
 
@@ -154,18 +151,15 @@ assert (model.dof(aSketchFeature) == 2)
 # Change a distance value (check previous constraint is applied too)
 #=========================================================================
 d = DISTANCE2
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE2 = d
     aDistance.setValue(DISTANCE2)
     aSession.finishOperation()
-    if DISTANCE2 == 0:
-        assert(aVDist2.error() != "")
-    else:
-        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)
+    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)
 
@@ -216,16 +210,13 @@ assert (model.dof(aSketchFeature) == 6)
 # Change a distance value
 #=========================================================================
 d = DISTANCE3
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE3 = d
     aDistance.setValue(DISTANCE3)
     aSession.finishOperation()
-    if DISTANCE3 == 0:
-        assert(aVDist3.error() != "")
-    else:
-        assert math.fabs(verticalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aStartPoint, aEndPoint), DISTANCE3)
+    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)
 
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceVerticalZero.py b/src/SketchPlugin/Test/TestConstraintDistanceVerticalZero.py
new file mode 100644 (file)
index 0000000..7578a72
--- /dev/null
@@ -0,0 +1,122 @@
+# 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
+#
+
+"""
+    Test the zero value of the constraint "DistanceVertical"
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from SketchAPI import *
+
+__updated__ = "2019-10-22"
+TOLERANCE = 1.e-6
+
+class TestZeroDistanceVertical(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myLine1 = self.mySketch.addLine(10, 10, 45, 27.5)
+    self.myLine2 = self.mySketch.addLine(20, 15, 30, 40)
+    self.myLine3 = self.mySketch.addLine(0, 10, 10, 10)
+    model.do()
+    self.myDOF = 12
+
+  def tearDown(self):
+    model.end()
+    model.checkSketch(self.mySketch, self.myDOF)
+
+  def assertDistanceVertical(self, theObject1, theObject2, theDistance):
+    dist = theObject2.y() - theObject1.y()
+    self.assertTrue(math.fabs(dist - theDistance) < TOLERANCE, "Current distance = {}, reference = {}".format(dist, theDistance))
+    model.checkSketch(self.mySketch, self.myDOF)
+
+
+  def test_distance_positive_nzznz(self):
+    """ Test 1. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.startPoint(), self.myLine2.startPoint(), 5)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine2.startPoint(), 5)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine2.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+
+  def test_distance_negative_nzznz(self):
+    """ Test 2. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.endPoint(), self.myLine2.startPoint(), 12.5)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.endPoint(), self.myLine2.startPoint(), -12.5)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.endPoint(), self.myLine2.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(12.5)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.endPoint(), self.myLine2.startPoint(), -12.5)
+
+  def test_distance_equal_znzz(self):
+    """ Test 3. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.endPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+  def test_distance_notequal_znzz(self):
+    """ Test 4. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.startPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceZero.py b/src/SketchPlugin/Test/TestConstraintDistanceZero.py
new file mode 100644 (file)
index 0000000..cf9a69d
--- /dev/null
@@ -0,0 +1,188 @@
+# 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
+#
+
+"""
+    Test the zero value of the constraint "Distance"
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from SketchAPI import *
+
+__updated__ = "2019-10-22"
+TOLERANCE = 1.e-6
+
+class TestZeroDistance(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myLine1 = self.mySketch.addLine(10, 10, 45, 27.5)
+    self.myLine2 = self.mySketch.addLine(20, 15, 30, 40)
+    self.myLine3 = self.mySketch.addLine(10, 0, 10, 10)
+    model.do()
+    self.myDOF = 12
+
+  def tearDown(self):
+    model.end()
+    model.checkSketch(self.mySketch, self.myDOF)
+
+  def assertDistance(self, theObject1, theObject2, theDistance, isSigned = False):
+    dist = -1.
+    if issubclass(type(theObject1), SketchAPI_SketchEntity):
+      if isSigned:
+        dist = model.signedDistancePointLine(theObject2, theObject1)
+      else:
+        dist = model.distancePointLine(theObject2, theObject1)
+    elif issubclass(type(theObject2), SketchAPI_SketchEntity):
+      if isSigned:
+        dist = model.signedDistancePointLine(theObject1, theObject2)
+      else:
+        dist = model.distancePointLine(theObject1, theObject2)
+    else:
+      dist = model.distancePointPoint(theObject1, theObject2)
+    self.assertTrue(math.fabs(dist - theDistance) < TOLERANCE, "Current distance = {}, reference = {}".format(dist, theDistance))
+    model.checkSketch(self.mySketch, self.myDOF)
+
+
+  def test_distance_points_nzznz(self):
+    """ Test 1. Change point-point distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 20)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 20)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    self.myDOF += 1
+    model.do()
+    self.assertDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 20)
+
+  def test_distance_points_znzz(self):
+    """ Test 2. Change point-point distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+    self.myDOF -= 2
+    model.do()
+    self.assertDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    self.myDOF += 1
+    model.do()
+    self.assertDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+
+  def test_unsigned_distance_nzznz(self):
+    """ Test 3. Change unsigned point-line distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.result(), self.myLine2.endPoint(), 20)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 20)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 20)
+
+  def test_unsigned_distance_znzz(self):
+    """ Test 4. Change unsigned point-line distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setDistance(self.myLine2.startPoint(), self.myLine1.result(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+
+  def test_signed_distance_nzznz(self):
+    """ Test 5. Change signed point-line distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.result(), self.myLine2.endPoint(), 20, True)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), -20, True)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 0, True)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), -20, True)
+
+  def test_signed_distance_nzznz_2(self):
+    """ Test 6. Change signed point-line distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine3.startPoint(), self.myLine1.result(), 10, True)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine3.startPoint(), 10, True)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine3.startPoint(), 0, True)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine3.startPoint(), 20, True)
+
+  def test_signed_distance_znzz(self):
+    """ Test 7. Change signed point-line distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setDistance(self.myLine2.startPoint(), self.myLine1.result(), 0, True)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
index eef35764893f6b9a8407b37b0b0262a9f1d89fe4..39f2a2b80cb05ccd28a79f330fc1923fc7210de8 100644 (file)
           <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityB"/>
           <validator id="PartSet_DifferentObjects"/>
           <validator id="GeomValidators_ShapeType" parameters="vertex,line"/>
-          <validator id="PartSet_DifferentPoints" parameters="ConstraintEntityB"/>
         </sketch_shape_selector>
         <sketch_shape_selector
           id="ConstraintEntityB"
           <validator id="SketchPlugin_DistanceAttr" parameters="ConstraintEntityA"/>
           <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityA"/>
           <validator id="GeomValidators_ShapeType" parameters="vertex,line"/>
-          <validator id="PartSet_DifferentPoints" parameters="ConstraintEntityA"/>
         </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" min="0">
-          <validator id="GeomValidators_Positive"/>
+          <validator id="GeomValidators_Positive" parameters="-1.e-10"/>
         </doublevalue_editor>
 
         <module_choice id="LocationType"
         <sketch-2dpoint_flyout_selector id="ConstraintFlyoutValuePnt"  default="computed" internal="1" obligatory="0"/>
 
         <doublevalue_editor label="Value" tooltip="Distance" id="DistanceValue" default="computed" min="0">
-          <validator id="GeomValidators_Positive"/>
+          <validator id="GeomValidators_Positive" parameters="-1.e-10"/>
         </doublevalue_editor>
 
         <module_choice id="LocationType"
         <sketch-2dpoint_flyout_selector id="ConstraintFlyoutValuePnt"  default="computed" internal="1" obligatory="0"/>
 
         <doublevalue_editor label="Value" tooltip="Distance" id="DistanceValue" default="computed" min="0">
-          <validator id="GeomValidators_Positive"/>
+          <validator id="GeomValidators_Positive" parameters="-1.e-10"/>
         </doublevalue_editor>
 
         <module_choice id="LocationType"
index 6e6a6642f44f92a1eb6fd814da61c010fe203f37..de38dfcdb087bc79945b049645e5c84998a34ac9 100644 (file)
@@ -54,5 +54,5 @@ void PlaneGCSSolver_ConstraintWrapper::setValue(const double& theValue)
 
 double PlaneGCSSolver_ConstraintWrapper::value() const
 {
-  return myValueParam->value();
+  return myValueParam ? myValueParam->value() : 0.0;
 }
index 0850933a000bdfb5e2847c328412462f2e5c2c2b..29568114d22fe8d033ab6e7054ccdb5142fa6fcf 100644 (file)
@@ -25,6 +25,7 @@
 #include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_Storage.h>
 #include <PlaneGCSSolver_Tools.h>
+#include <PlaneGCSSolver_UpdateCoincidence.h>
 
 #include <SketchPlugin_ConstraintDistanceHorizontal.h>
 #include <SketchPlugin_ConstraintDistanceVertical.h>
@@ -71,6 +72,86 @@ static void adjustOddPoint(const EntityWrapperPtr& theDistPoint,
   *(theOddPoint->y) = aProjectedPnt->y();
 }
 
+static FeaturePtr getFeature(AttributeRefAttrPtr theRefAttr)
+{
+  ObjectPtr anObj;
+  if (theRefAttr->isObject())
+    anObj = theRefAttr->object();
+  else
+    anObj = theRefAttr->attr()->owner();
+  return ModelAPI_Feature::feature(anObj);
+}
+
+static void calculateDistanceDirection(const ConstraintPtr& theConstraint,
+                                       const StoragePtr& theStorage,
+                                       double& theDirX, double& theDirY)
+{
+  std::shared_ptr<GeomDataAPI_Dir> aDistDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+      theConstraint->attribute(SketchPlugin_ConstraintDistance::DIRECTION_ID()));
+  if (aDistDir && aDistDir->isInitialized()) {
+    theDirX = aDistDir->x();
+    theDirY = aDistDir->y();
+    if (fabs(theDirX) > tolerance || fabs(theDirY) > tolerance)
+      return;
+  }
+
+  AttributeRefAttrPtr aRefAttrA = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
+  AttributeRefAttrPtr aRefAttrB = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
+
+  EntityWrapperPtr aEntityA = theStorage->entity(aRefAttrA);
+  EntityWrapperPtr aEntityB = theStorage->entity(aRefAttrB);
+
+  GCSPointPtr aPoint;
+  if (aEntityA->type() != ENTITY_LINE && aEntityB->type() != ENTITY_LINE) {
+    aPoint = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(aEntityA)->point();
+    theDirX = 1.0;
+    theDirY = 0.0;
+
+    EdgeWrapperPtr anEdgeA = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(
+        theStorage->entity(getFeature(aRefAttrA)));
+    EdgeWrapperPtr anEdgeB = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(
+        theStorage->entity(getFeature(aRefAttrB)));
+
+    if (anEdgeA && anEdgeB) {
+      GCS::DeriVector2 aDirA = anEdgeA->entity()->CalculateNormal(*aPoint);
+      GCS::DeriVector2 aDirB = anEdgeB->entity()->CalculateNormal(*aPoint);
+      double x = -aDirA.x + aDirB.x;
+      double y = -aDirA.y + aDirB.y;
+      double norm = sqrt(x*x + y*y);
+      if (norm > tolerance) {
+        theDirX = x / norm;
+        theDirY = y / norm;
+      }
+    }
+  }
+}
+
+static void moveEntity(const ConstraintPtr& theConstraint,
+                       const StoragePtr& theStorage,
+                       const double theDX, const double theDY)
+{
+  static const double THE_SHIFT = 1.e-4;
+
+  AttributeRefAttrPtr aRefAttrA = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
+  AttributeRefAttrPtr aRefAttrB = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
+
+  EntityWrapperPtr aEntityA = theStorage->entity(aRefAttrA);
+  EntityWrapperPtr aEntityB = theStorage->entity(aRefAttrB);
+
+  PointWrapperPtr aPointA = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(aEntityA);
+  PointWrapperPtr aPointB = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(aEntityB);
+
+  if (aPointA) {
+    *aPointA->point()->x -= THE_SHIFT * theDX;
+    *aPointA->point()->y -= THE_SHIFT * theDY;
+  }
+  else if (aPointB) {
+    *aPointB->point()->x += THE_SHIFT * theDX;
+    *aPointB->point()->y += THE_SHIFT * theDY;
+  }
+}
+
+
 
 void SketchSolver_ConstraintDistance::getAttributes(
     EntityWrapperPtr& theValue,
@@ -82,21 +163,30 @@ void SketchSolver_ConstraintDistance::getAttributes(
     return;
   }
 
+  ScalarWrapperPtr aValue = std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(theValue);
+  bool isCoincidence = fabs(aValue->value()) < tolerance;
+
   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;
+      myType = isCoincidence ? CONSTRAINT_PT_PT_COINCIDENT : CONSTRAINT_PT_PT_DISTANCE;
   } else if (theAttributes[2] && theAttributes[2]->type() == ENTITY_LINE)
-    myType = CONSTRAINT_PT_LINE_DISTANCE;
+    myType = isCoincidence ? CONSTRAINT_PT_ON_CURVE : CONSTRAINT_PT_LINE_DISTANCE;
   else
     theAttributes.clear();
 
+  if (myType == CONSTRAINT_HORIZONTAL_DISTANCE || myType == CONSTRAINT_VERTICAL_DISTANCE)
+    mySignValue = aValue->value() < 0.0 ? -1.0 : 1.0;
+
   myPrevValue = 0.0;
 
-  myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateFeature::GROUP());
+  if (isCoincidence)
+    myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
+  else
+    myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateFeature::GROUP());
 }
 
 void SketchSolver_ConstraintDistance::adjustConstraint()
@@ -127,7 +217,41 @@ void SketchSolver_ConstraintDistance::update()
   ConstraintWrapperPtr aConstraint = myStorage->constraint(myBaseConstraint);
   myPrevValue = aConstraint->value();
 
-  SketchSolver_Constraint::update();
+  bool isDistanceAlognDir =
+    myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID() ||
+    myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID();
+
+  AttributeDoublePtr aCurValue = myBaseConstraint->real(SketchPlugin_Constraint::VALUE());
+  bool isZeroSwitch = fabs(myPrevValue) < tolerance && fabs(aCurValue->value()) > tolerance;
+  bool isNonZeroSwitch = fabs(myPrevValue) > tolerance && fabs(aCurValue->value()) < tolerance;
+
+  if (!isDistanceAlognDir && (isZeroSwitch || isNonZeroSwitch)) {
+    // the value is changed from non-zero to zero or vice versa
+    remove();
+    process();
+
+    // move entities to avoid conflicting constraints
+    if (isZeroSwitch) {
+      double aDirX, aDirY;
+      // calculate the direction basing on the distanced objects
+      calculateDistanceDirection(myBaseConstraint, myStorage, aDirX, aDirY);
+      moveEntity(myBaseConstraint, myStorage, aDirX, aDirY);
+
+      if (myOddPoint) {
+        removeConstraintsKeepingSign();
+        addConstraintsToKeepSign();
+      }
+    }
+  }
+  else {
+    SketchSolver_Constraint::update();
+    if (isDistanceAlognDir && mySignValue * aConstraint->value() < 0.0) {
+      if (isZeroSwitch)
+        aConstraint->setValue(-aConstraint->value());
+      else
+        mySignValue *= -1.0;
+    }
+  }
 }
 
 bool SketchSolver_ConstraintDistance::remove()
index d4178917e8194b586d8d92e2403940328d23ba04..b15f0d05f0459ece2a964e17187769c4d103cbcb 100644 (file)
@@ -137,6 +137,20 @@ bool SketcherPrs_LengthDimension::IsReadyToDisplay(ModelAPI_Feature* theConstrai
   return readyToDisplay(theConstraint, thePlane, aPnt1, aPnt2);
 }
 
+static bool isEqualPoints(ModelAPI_Feature* theConstraint,
+                          const gp_Pnt& thePoint1,
+                          const gp_Pnt& thePoint2)
+{
+  bool isEqual = false;
+  if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID())
+    isEqual = Abs(thePoint1.X() - thePoint2.X()) < Precision::Confusion();
+  else if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID())
+    isEqual = Abs(thePoint1.Y() - thePoint2.Y()) < Precision::Confusion();
+  else
+    isEqual = thePoint1.SquareDistance(thePoint2) < Precision::SquareConfusion();
+  return isEqual;
+}
+
 void SketcherPrs_LengthDimension::Compute(
   const Handle(PrsMgr_PresentationManager3d)& thePresentationManager,
   const Handle(Prs3d_Presentation)& thePresentation,
@@ -147,6 +161,28 @@ void SketcherPrs_LengthDimension::Compute(
   gp_Pnt aPnt1, aPnt2;
   bool aReadyToDisplay = readyToDisplay(myConstraint, plane(), aPnt1, aPnt2);
   if (aReadyToDisplay) {
+    if (isEqualPoints(myConstraint, aPnt1, aPnt2)) {
+      // adjust points to draw the dimension presentation
+      std::shared_ptr<GeomDataAPI_Dir> aDirAttr = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+          myConstraint->attribute(SketchPlugin_ConstraintDistance::DIRECTION_ID()));
+      double x = 0.0, y = 0.0;
+      if (aDirAttr && aDirAttr->isInitialized()) {
+        x = aDirAttr->x();
+        y = aDirAttr->y();
+        if (x == 0.0 && y == 0.0)
+          x = 1.0;
+      }
+      else if (myConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID())
+        y = 1.0;
+      else
+        x = 1.0;
+      GeomPointPtr aCoord = plane()->to3D(x, y);
+
+      gp_XYZ aDir(aCoord->x(), aCoord->y(), aCoord->z());
+      aPnt1.ChangeCoord().Add(aDir * (-Precision::Confusion()));
+      aPnt2.ChangeCoord().Add(aDir * Precision::Confusion());
+    }
+
     myFirstPoint = aPnt1;
     mySecondPoint = aPnt2;
 
@@ -267,14 +303,14 @@ bool SketcherPrs_LengthDimension::readyToDisplay(ModelAPI_Feature* theConstraint
     if (!aPnt_A || !aPnt_B) // Objects not found
       return false;
 
-    if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID()) {
+    /*if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID()) {
       if (fabs(aPnt_A->x() - aPnt_B->x()) < Precision::Confusion())
         return false;
     }
     else if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID()) {
       if (fabs(aPnt_A->y() - aPnt_B->y()) < Precision::Confusion())
         return false;
-    }
+    }*/
 
     // Get points from these object
     std::shared_ptr<GeomAPI_Pnt> aPoint1 = thePlane->to3D(aPnt_A->x(), aPnt_A->y());