Salome HOME
Task 2.11. Ability to impose a midpoint on an arc (refers issue #3002)
authorazv <azv@opencascade.com>
Wed, 4 Sep 2019 05:57:55 +0000 (08:57 +0300)
committerazv <azv@opencascade.com>
Wed, 4 Sep 2019 05:57:55 +0000 (08:57 +0300)
19 files changed:
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_Validators.cpp
src/SketchPlugin/Test/TestConstraintMiddlePointOnArc.py [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/PlaneGCSSolver/CMakeLists.txt
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.cpp [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.h [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_FeatureBuilder.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/SketchSolver_ConstraintMiddle.cpp
src/SketchSolver/SketchSolver_ConstraintMiddle.h
src/SketchSolver/SketchSolver_Group.cpp
src/SketchSolver/SketchSolver_Storage.h

index cd9bc43fb6480b23783e8f493cce1ec0540db8b9..26cf523f22870505faa102b59b51d12666ab0fd9 100644 (file)
@@ -212,6 +212,7 @@ ADD_UNIT_TESTS(
   TestConstraintHorizontalValidator.py
   TestConstraintLength.py
   TestConstraintMiddlePoint.py
+  TestConstraintMiddlePointOnArc.py
   TestConstraintParallel.py
   TestConstraintPerpendicular.py
   TestConstraintPerpendicularArcLine.py
index a0cbf6805babe131cb0468d78aedc8da5e308e11..fb9be5197ed7a6dcf6272367797871a077953655 100644 (file)
@@ -778,7 +778,8 @@ bool SketchPlugin_MiddlePointAttrValidator::isValid(const AttributePtr& theAttri
 
       if (aFeature->getKind() == SketchPlugin_Point::ID())
         ++aNbPoints;
-      else if (aFeature->getKind() == SketchPlugin_Line::ID())
+      else if (aFeature->getKind() == SketchPlugin_Line::ID() ||
+               aFeature->getKind() == SketchPlugin_Arc::ID())
         ++aNbLines;
     }
   }
diff --git a/src/SketchPlugin/Test/TestConstraintMiddlePointOnArc.py b/src/SketchPlugin/Test/TestConstraintMiddlePointOnArc.py
new file mode 100644 (file)
index 0000000..2688932
--- /dev/null
@@ -0,0 +1,252 @@
+# Copyright (C) 2017-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test middle point on an arc
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from GeomAPI import GeomAPI_Dir2d
+
+__updated__ = "2019-09-03"
+
+class TestMiddlePointOnArc(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myTestPassed = True
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myArc = self.mySketch.addArc(50, 50, 70, 50, 50, 70, False)
+    self.myLine = self.mySketch.addLine(55, 60, 50, 0)
+    self.myDOF = 9
+    model.do()
+    self.checkDOF()
+
+  def tearDown(self):
+    if self.myTestPassed:
+      model.assertArcValidity(self.myArc)
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      self.checkDOF()
+    model.end()
+    assert(model.checkPythonDump())
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkMiddlePoint(self, thePoint, theArc):
+    self.myTestPassed = False
+    # check point on arc
+    dist = thePoint.pnt().distance(theArc.center().pnt())
+    NB_DIGITS = 7 - math.floor(math.log10(theArc.radius().value()))
+    self.assertAlmostEqual(dist, theArc.radius().value(), NB_DIGITS)
+    # check middle point
+    dirPC = GeomAPI_Dir2d(thePoint.x() - theArc.center().x(),
+                          thePoint.y() - theArc.center().y())
+    dirSC = GeomAPI_Dir2d(theArc.startPoint().x() - theArc.center().x(),
+                          theArc.startPoint().y() - theArc.center().y())
+    dirEC = GeomAPI_Dir2d(theArc.endPoint().x() - theArc.center().x(),
+                          theArc.endPoint().y() - theArc.center().y())
+    angleSP = dirSC.angle(dirPC)
+    anglePE = dirPC.angle(dirEC)
+    self.assertAlmostEqual(angleSP, anglePE)
+    self.assertEqual(angleSP < 0, theArc.reversed().value())
+    self.myTestPassed = True
+
+  def rotatePoint(self, thePoint, theCenter, theAngle):
+    dirX = thePoint.x() - theCenter.x()
+    dirY = thePoint.y() - theCenter.y()
+    newX = theCenter.x() + dirX * math.cos(theAngle) - dirY * math.sin(theAngle)
+    newY = theCenter.y() + dirX * math.sin(theAngle) + dirY * math.cos(theAngle)
+    self.mySketch.move(thePoint, newX, newY)
+
+  def moveArc(self):
+    ANGLE_STEP = math.pi * 5.0 / 180.0
+    ANGLE_THRESHOLD = math.pi
+    # move start point of the arc clockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.startPoint(), self.myArc.center(), -ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+    # move start point of the arc conterclockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.startPoint(), self.myArc.center(), ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+
+    # move end point of the arc clockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.endPoint(), self.myArc.center(), -ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+    # move end point of the arc conterclockwise
+    fullAngle = 0.0
+    while fullAngle < ANGLE_THRESHOLD:
+      self.rotatePoint(self.myArc.endPoint(), self.myArc.center(), ANGLE_STEP)
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+      fullAngle += ANGLE_STEP
+
+    # move center of the arc
+    DELTA = [1.0, 1.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myArc.center(), self.myArc.center().x() + DELTA[0], self.myArc.center().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    DELTA = [-1.0, 1.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myArc.center(), self.myArc.center().x() + DELTA[0], self.myArc.center().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+
+  def moveLine(self):
+    DELTA = [1.0, 0.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myLine.startPoint(), self.myLine.startPoint().x() + DELTA[0], self.myLine.startPoint().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    DELTA = [0.0, 1.0]
+    for i in range(0, 40):
+      if i == 10 or i == 30:
+        DELTA = [-DELTA[0], -DELTA[1]]
+      self.mySketch.move(self.myLine.startPoint(), self.myLine.startPoint().x() + DELTA[0], self.myLine.startPoint().y() + DELTA[1])
+      model.do()
+      self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+
+
+  def test_middle_point_PA(self):
+    """ Test 1. Set middle point constraint (point is the first argument)
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+
+  def test_middle_point_AP(self):
+    """ Test 2. Set middle point constraint (point is the second argument)
+    """
+    self.mySketch.setMiddlePoint(self.myArc.results()[1], self.myLine.startPoint())
+    self.myDOF -= 2
+    model.do()
+
+  def test_coincident_middle_point(self):
+    """ Test 3. Set middle point constraint for the point already coincident with the arc
+    """
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+
+  def test_middle_point_coincident(self):
+    """ Test 4. Set concidence of the point and the arc which are already constrained with middle point
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+
+  @unittest.expectedFailure
+  def test_middle_point_limitation(self):
+    """ Test 5. Check middle point fails if the point's coordinates are equal to the arc boundary point
+    """
+    self.myLine.startPoint().setValue(self.myArc.endPoint().pnt())
+    model.do()
+    coincidence = self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    # this check will fail due to the limitation of PlanGCS
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+
+  def test_middle_point_move_arc(self):
+    """ Test 6. Set middle point constraint and move arc
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveArc()
+
+  def test_middle_point_coincidence_move_arc(self):
+    """ Test 7. Set coincidence and middle point constraint and move arc
+    """
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveArc()
+
+  def test_middle_point_move_line(self):
+    """ Test 8. Set middle point constraint and move line
+    """
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveLine()
+
+  def test_middle_point_coincidence_move_line(self):
+    """ Test 9. Set coincidence and middle point constraint and move line
+    """
+    self.mySketch.setCoincident(self.myLine.startPoint(), self.myArc.results()[1])
+    model.do()
+    self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.moveLine()
+
+  def test_remove_middle_point(self):
+    """ Test 10. Set and then remove middle point constraint
+    """
+    mp = self.mySketch.setMiddlePoint(self.myLine.startPoint(), self.myArc.results()[1])
+    self.myDOF -= 2
+    model.do()
+    model.assertArcValidity(self.myArc)
+    self.checkMiddlePoint(self.myLine.startPoint(), self.myArc)
+    self.checkDOF()
+    # remove middle point
+    self.myDocument.removeFeature(mp.feature())
+    self.myDOF += 2
+    model.do()
+    self.checkDOF()
+    # set flag False to avoid checking middle point constraint in tearDown() method
+    self.myTestPassed = False
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
index 76871ab1beb1b9bbcb9d40a479f53cfb6fed1850..35a8e7a76107fda527e6900103fe66d767980579 100644 (file)
     </group>
 
     <group id="Dimensional constraints">
-    <!--  SketchConstraintDistance  -->
+      <!--  SketchConstraintDistance  -->
       <feature
         id="SketchConstraintDistance"
         title="Distance"
         <validator id="PartSet_ParallelSelection"/>
       </feature>
 
-    <!--  SketchConstraintPerpendicular  -->
+      <!--  SketchConstraintPerpendicular  -->
       <feature id="SketchConstraintPerpendicular" title="Perpendicular"
                tooltip="Create constraint defining two orthogonal objects"
                icon="icons/Sketch/perpendicular.png"
         <validator id="PartSet_EqualSelection"/>
       </feature>
 
-    <!--  SketchConstraintCollinear  -->
+      <!--  SketchConstraintCollinear  -->
       <feature id="SketchConstraintCollinear" title="Collinear" tooltip="Create constraint defining collinearity of two lines"
                icon="icons/Sketch/collinear.png"
                helpfile="collinearFeature.html">
index 7a961fcad378ae7db1a3048a2b30258e77514f20..af72c4b13ae4870766f82a8dec15ce2b84fdfcda 100644 (file)
@@ -29,6 +29,7 @@ SET(PLANEGCSSOLVER_HEADERS
     PlaneGCSSolver_PointWrapper.h
     PlaneGCSSolver_ScalarWrapper.h
     PlaneGCSSolver_AngleWrapper.h
+    PlaneGCSSolver_BooleanWrapper.h
     PlaneGCSSolver_Tools.h
 )
 
@@ -40,6 +41,7 @@ SET(PLANEGCSSOLVER_SOURCES
     PlaneGCSSolver_PointWrapper.cpp
     PlaneGCSSolver_ScalarWrapper.cpp
     PlaneGCSSolver_AngleWrapper.cpp
+    PlaneGCSSolver_BooleanWrapper.cpp
     PlaneGCSSolver_Tools.cpp
 )
 
index ea6eef78c0d289b9c0f6b0b698f5966c46bdf60a..ca463c53d70f063745d9f062bf7c250cc6810d24 100644 (file)
@@ -21,6 +21,7 @@
 #include <PlaneGCSSolver_AttributeBuilder.h>
 #include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 
 #include <GeomDataAPI_Point2D.h>
 #include <ModelAPI_AttributeDouble.h>
@@ -44,6 +45,16 @@ static double* createParameter(PlaneGCSSolver_Storage* theStorage)
   return theStorage ? theStorage->createParameter() : (new double(0));
 }
 
+static EntityWrapperPtr createBoolean(const AttributePtr& theAttribute)
+{
+  BooleanWrapperPtr aWrapper;
+  AttributeBooleanPtr aBoolean =
+      std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
+  if (aBoolean)
+    aWrapper = BooleanWrapperPtr(new PlaneGCSSolver_BooleanWrapper(aBoolean->value()));
+  return aWrapper;
+}
+
 static EntityWrapperPtr createScalar(const AttributePtr&     theAttribute,
                                      PlaneGCSSolver_Storage* theStorage)
 {
@@ -99,6 +110,8 @@ EntityWrapperPtr PlaneGCSSolver_AttributeBuilder::createAttribute(
     aResult = createPoint(theAttribute, myStorage);
   if (!aResult)
     aResult = createScalar(theAttribute, myStorage);
+  if (!aResult)
+    aResult = createBoolean(theAttribute);
   if (aResult && !myStorage)
     aResult->setExternal(true);
   return aResult;
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.cpp
new file mode 100644 (file)
index 0000000..225c434
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <PlaneGCSSolver_BooleanWrapper.h>
+
+PlaneGCSSolver_BooleanWrapper::PlaneGCSSolver_BooleanWrapper(bool theParam)
+  : myValue(theParam)
+{
+}
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.h
new file mode 100644 (file)
index 0000000..3bd1d8d
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef PlaneGCSSolver_BooleanWrapper_H_
+#define PlaneGCSSolver_BooleanWrapper_H_
+
+#include <PlaneGCSSolver_Defs.h>
+#include <PlaneGCSSolver_EntityWrapper.h>
+
+/**
+ *  Wrapper providing storage for boolean values.
+ */
+class PlaneGCSSolver_BooleanWrapper : public PlaneGCSSolver_EntityWrapper
+{
+public:
+  PlaneGCSSolver_BooleanWrapper(bool theValue);
+
+  /// \brief Change value of parameter
+  void setValue(bool theValue)
+  { myValue = theValue; }
+  /// \brief Return value of parameter
+  bool value() const
+  { return myValue; }
+
+  /// \brief Return type of current entity
+  virtual SketchSolver_EntityType type() const
+  { return ENTITY_BOOLEAN; }
+
+protected:
+  bool myValue;
+};
+
+typedef std::shared_ptr<PlaneGCSSolver_BooleanWrapper> BooleanWrapperPtr;
+
+#endif
index fccd4d718b0cea8c7dd3eeb43275825a3f3fef60..e4851a295ecabfb4ef47d728373ed6a2874ef584 100644 (file)
@@ -44,6 +44,7 @@ const ConstraintID CID_FICTIVE = 1024;
 /// Types of entities
 enum SketchSolver_EntityType {
   ENTITY_UNKNOWN = 0,
+  ENTITY_BOOLEAN,
   ENTITY_SCALAR,
   ENTITY_ANGLE,
   ENTITY_POINT,
index 7ec36cf236a33507de548cfad7bde4f137c137a5..2efea191456fa7aef9b35197a660a5cd65b37231 100644 (file)
@@ -21,7 +21,8 @@
 #include <cmath>
 
 PlaneGCSSolver_EdgeWrapper::PlaneGCSSolver_EdgeWrapper(const GCSCurvePtr theEntity)
-  : myEntity(theEntity)
+  : myEntity(theEntity),
+    myReversed(false)
 {
   std::shared_ptr<GCS::Line> aLine = std::dynamic_pointer_cast<GCS::Line>(myEntity);
   if (aLine) myType = ENTITY_LINE;
index 9cd5bc2cbe972483cda4a46702b463805656df6c..e84b809c60ee26d62a52bc8422c42d0e76f9d86c 100644 (file)
@@ -21,6 +21,7 @@
 #define PlaneGCSSolver_EdgeWrapper_H_
 
 #include <PlaneGCSSolver_Defs.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 #include <PlaneGCSSolver_EntityWrapper.h>
 
 /**
@@ -44,9 +45,16 @@ public:
 
   bool isDegenerated() const;
 
+  void setReversed(BooleanWrapperPtr theReversed)
+  { myReversed = theReversed; }
+
+  bool isReversed() const
+  { return myReversed ? myReversed->value() : false; }
+
 private:
   SketchSolver_EntityType myType;
   GCSCurvePtr myEntity;
+  BooleanWrapperPtr myReversed; // preferably used to control arc orientation
 };
 
 typedef std::shared_ptr<PlaneGCSSolver_EdgeWrapper> EdgeWrapperPtr;
index c2965e3eed652e0ada68cbee33756ff294a85b8d..f7687f3e7318ae73ca577472b16465c01fe28c3e 100644 (file)
@@ -21,6 +21,7 @@
 #include <PlaneGCSSolver_EdgeWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 
 #include <SketchPlugin_Arc.h>
 #include <SketchPlugin_Circle.h>
@@ -162,21 +163,25 @@ EntityWrapperPtr createArc(const AttributeEntityMap&    theAttributes,
                            PlaneGCSSolver_Storage*      theStorage)
 {
   std::shared_ptr<GCS::Arc> aNewArc(new GCS::Arc);
+  BooleanWrapperPtr isReversed = false;
 
   // Base attributes of arc (center, start and end points)
   AttributeEntityMap::const_iterator anIt = theAttributes.begin();
   for (; anIt != theAttributes.end(); ++anIt) {
     std::shared_ptr<PlaneGCSSolver_PointWrapper> aPoint =
         std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(anIt->second);
-    if (!aPoint)
-      continue;
-
-    if (anIt->first->id() == SketchPlugin_Arc::CENTER_ID())
-      aNewArc->center = *(aPoint->point());
-    else if (anIt->first->id() == SketchPlugin_Arc::START_ID())
-      aNewArc->start = *(aPoint->point());
-    else if (anIt->first->id() == SketchPlugin_Arc::END_ID())
-      aNewArc->end = *(aPoint->point());
+    if (aPoint) {
+      if (anIt->first->id() == SketchPlugin_Arc::CENTER_ID())
+        aNewArc->center = *(aPoint->point());
+      else if (anIt->first->id() == SketchPlugin_Arc::START_ID())
+        aNewArc->start = *(aPoint->point());
+      else if (anIt->first->id() == SketchPlugin_Arc::END_ID())
+        aNewArc->end = *(aPoint->point());
+    }
+    else {
+      // reversed flag
+      isReversed = std::dynamic_pointer_cast<PlaneGCSSolver_BooleanWrapper>(anIt->second);
+    }
   }
 
   // Additional atrtributes of arc necessary for PlaneGCS solver
@@ -200,7 +205,9 @@ EntityWrapperPtr createArc(const AttributeEntityMap&    theAttributes,
       new GeomAPI_Dir2d((*aNewArc->end.x) - aCenter->x(), (*aNewArc->end.y) - aCenter->y()));
   *aNewArc->endAngle = OX->angle(aDir);
 
-  return EntityWrapperPtr(new PlaneGCSSolver_EdgeWrapper(aNewArc));
+  EdgeWrapperPtr anArcWrapper(new PlaneGCSSolver_EdgeWrapper(aNewArc));
+  anArcWrapper->setReversed(isReversed);
+  return anArcWrapper;
 }
 
 bool isAttributeApplicable(const std::string& theAttrName, const std::string& theOwnerName)
@@ -208,7 +215,8 @@ bool isAttributeApplicable(const std::string& theAttrName, const std::string& th
   if (theOwnerName == SketchPlugin_Arc::ID()) {
     return theAttrName == SketchPlugin_Arc::CENTER_ID() ||
            theAttrName == SketchPlugin_Arc::START_ID() ||
-           theAttrName == SketchPlugin_Arc::END_ID();
+           theAttrName == SketchPlugin_Arc::END_ID() ||
+           theAttrName == SketchPlugin_Arc::REVERSED_ID();
   }
   else if (theOwnerName == SketchPlugin_Circle::ID()) {
     return theAttrName == SketchPlugin_Circle::CENTER_ID() ||
index 2f98c88138f5b3814eea5ddea799ec05508d6c5c..5c8cebe53ff3a75fc0fa3ac47e2419adc3867411 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <PlaneGCSSolver_Storage.h>
 #include <PlaneGCSSolver_Solver.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 #include <PlaneGCSSolver_ConstraintWrapper.h>
 #include <PlaneGCSSolver_EdgeWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
@@ -134,6 +135,15 @@ static bool updateValues(AttributePtr& theAttribute, EntityWrapperPtr& theEntity
       isUpdated = updateValue(aScalar->value(), aValue);
       if (isUpdated)
         aWrapper->setValue(aValue);
+    } else {
+      AttributeBooleanPtr aBoolean =
+          std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
+      if (aBoolean) {
+        BooleanWrapperPtr aWrapper =
+            std::dynamic_pointer_cast<PlaneGCSSolver_BooleanWrapper>(theEntity);
+        isUpdated = aWrapper->value() != aBoolean->value();
+        aWrapper->setValue(aBoolean->value());
+      }
     }
   }
 
@@ -193,7 +203,8 @@ bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
   std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
   for (; anAttrIt != anAttributes.end(); ++anAttrIt)
     if ((*anAttrIt)->attributeType() == GeomDataAPI_Point2D::typeId() ||
-        (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId())
+        (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId() ||
+        (*anAttrIt)->attributeType() == ModelAPI_AttributeBoolean::typeId())
       isUpdated = update(*anAttrIt) || isUpdated;
 
   // check external attribute is changed
@@ -211,30 +222,6 @@ bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
   if (sendNotify && isUpdated)
     notify(theFeature);
 
-  // update arc
-  if (aRelated && aRelated->type() == ENTITY_ARC) {
-    /// TODO: this code should be shared with FeatureBuilder somehow
-
-    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> anEntity =
-        std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aRelated);
-    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEntity->entity());
-
-    static std::shared_ptr<GeomAPI_Dir2d> OX(new GeomAPI_Dir2d(1.0, 0.0));
-    std::shared_ptr<GeomAPI_Pnt2d> aCenter(
-        new GeomAPI_Pnt2d(*anArc->center.x, *anArc->center.y));
-    std::shared_ptr<GeomAPI_Pnt2d> aStart(
-        new GeomAPI_Pnt2d(*anArc->start.x, *anArc->start.y));
-
-    *anArc->rad = aStart->distance(aCenter);
-
-    std::shared_ptr<GeomAPI_Dir2d> aDir(new GeomAPI_Dir2d(aStart->xy()->decreased(aCenter->xy())));
-    *anArc->startAngle = OX->angle(aDir);
-
-    aDir = std::shared_ptr<GeomAPI_Dir2d>(
-        new GeomAPI_Dir2d((*anArc->end.x) - aCenter->x(), (*anArc->end.y) - aCenter->y()));
-    *anArc->endAngle = OX->angle(aDir);
-  }
-
   return isUpdated;
 }
 
@@ -311,16 +298,16 @@ void PlaneGCSSolver_Storage::createArcConstraints(const EntityWrapperPtr& theArc
 
   // Additional constaints to fix arc's extra DoF (if the arc is not external):
   std::list<GCSConstraintPtr> anArcConstraints;
-  // 1. distances from center till start and end points are equal to radius
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
-      anArc->center, anArc->start, anArc->rad)));
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
-      anArc->center, anArc->end, anArc->rad)));
-  // 2. angles of start and end points should be equal to the arc angles
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
-      anArc->center, anArc->start, anArc->startAngle)));
-  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintP2PAngle(
-      anArc->center, anArc->end, anArc->endAngle)));
+  // constrain the start point on the arc
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->start, anArc->start.x, *anArc, anArc->startAngle)));
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->start, anArc->start.y, *anArc, anArc->startAngle)));
+  // constrain the end point on the arc
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->end, anArc->end.x, *anArc, anArc->endAngle)));
+  anArcConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+      anArc->end, anArc->end.y, *anArc, anArc->endAngle)));
 
   ConstraintWrapperPtr aWrapper(
       new PlaneGCSSolver_ConstraintWrapper(anArcConstraints, CONSTRAINT_UNKNOWN));
@@ -340,6 +327,39 @@ void PlaneGCSSolver_Storage::removeArcConstraints(const EntityWrapperPtr& theArc
   }
 }
 
+void PlaneGCSSolver_Storage::adjustParametrizationOfArcs()
+{
+  std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator anIt = myArcConstraintMap.begin();
+  for (; anIt != myArcConstraintMap.end(); ++anIt) {
+    EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(anIt->first);
+    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(anEdge->entity());
+    // tune start angle of the arc to be in [0, 2PI]
+    while (*anArc->startAngle < -PI)
+      *anArc->startAngle += 2.0 * PI;
+    while (*anArc->startAngle >= PI)
+      *anArc->startAngle -= 2.0 * PI;
+    // adjust end angle of the arc
+    if (anEdge->isReversed()) {
+      while (*anArc->endAngle > *anArc->startAngle)
+        *anArc->endAngle -= 2.0 * PI;
+      while (*anArc->endAngle + 2 * PI < *anArc->startAngle)
+        *anArc->endAngle += 2.0 * PI;
+    } else {
+      while (*anArc->endAngle < *anArc->startAngle)
+        *anArc->endAngle += 2.0 * PI;
+      while (*anArc->endAngle > *anArc->startAngle + 2 * PI)
+        *anArc->endAngle -= 2.0 * PI;
+    }
+  }
+
+  // update parameters of Middle point constraint for point on arc
+  std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator aCIt = myConstraintMap.begin();
+  for (; aCIt != myConstraintMap.end(); ++aCIt)
+    if (aCIt->second->type() == CONSTRAINT_MIDDLE_POINT) {
+      notify(aCIt->first);
+    }
+}
+
 
 bool PlaneGCSSolver_Storage::removeConstraint(ConstraintPtr theConstraint)
 {
index 3ac8e201e31be774171630d7be1a479b68af647b..107e4205dd89c20ccbbcf367bcf2ea9efce3b071 100644 (file)
@@ -90,6 +90,11 @@ public:
   virtual bool isEmpty() const
   { return SketchSolver_Storage::isEmpty() && myArcConstraintMap.empty(); }
 
+  /// \brief Make parametrization of arcs consistent.
+  ///        Forward arcs should have the last parameter greater than the first parameter.
+  ///        Reversed arcs should have the last parameter lesser than the first parameter.
+  virtual void adjustParametrizationOfArcs();
+
 private:
   /// \brief Convert feature using specified builder.
   EntityWrapperPtr createFeature(const FeaturePtr&             theFeature,
index 9c6cd276db56023706aa586818ae2920c270cd48..e90def0d2bce33d7fda69f0ee79ce14671aa1386 100644 (file)
@@ -106,7 +106,8 @@ static ConstraintWrapperPtr
                         std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theIntermed);
 static ConstraintWrapperPtr
   createConstraintMiddlePoint(std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
-                              std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity);
+                              std::shared_ptr<PlaneGCSSolver_EdgeWrapper>  theEntity,
+                              std::shared_ptr<PlaneGCSSolver_PointWrapper> theAuxParameters);
 
 static GCS::SET_pD scalarParameters(const ScalarWrapperPtr& theScalar);
 static GCS::SET_pD pointParameters(const PointWrapperPtr& thePoint);
@@ -191,7 +192,7 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
     aResult = createConstraintPointOnEntity(theType, aPoint1, GCS_EDGE_WRAPPER(theEntity1));
     break;
   case CONSTRAINT_MIDDLE_POINT:
-    aResult = createConstraintMiddlePoint(aPoint1, GCS_EDGE_WRAPPER(theEntity1));
+    aResult = createConstraintMiddlePoint(aPoint1, GCS_EDGE_WRAPPER(theEntity1), aPoint2);
     break;
   case CONSTRAINT_PT_PT_DISTANCE:
     aResult = createConstraintDistancePointPoint(GCS_SCALAR_WRAPPER(theValue), aPoint1, aPoint2);
@@ -354,19 +355,38 @@ ConstraintWrapperPtr createConstraintPointOnEntity(
 
 ConstraintWrapperPtr createConstraintMiddlePoint(
     std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
-    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity)
+    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity,
+    std::shared_ptr<PlaneGCSSolver_PointWrapper> theAuxParameters)
 {
+  std::list<GCSConstraintPtr> aConstrList;
+
   GCSPointPtr aPoint = thePoint->point();
   std::shared_ptr<GCS::Line> aLine = std::dynamic_pointer_cast<GCS::Line>(theEntity->entity());
-  if (!aLine)
-    return ConstraintWrapperPtr();
-
-  std::list<GCSConstraintPtr> aConstrList;
-  aConstrList.push_back(
-      GCSConstraintPtr(new GCS::ConstraintPointOnPerpBisector(*aPoint, aLine->p1, aLine->p2)));
-  aConstrList.push_back(GCSConstraintPtr(new GCS::ConstraintPointOnLine(*aPoint, *aLine)));
+  if (aLine) {
+    aConstrList.push_back(GCSConstraintPtr(new GCS::ConstraintPointOnLine(*aPoint, *aLine)));
+    aConstrList.push_back(
+        GCSConstraintPtr(new GCS::ConstraintPointOnPerpBisector(*aPoint, aLine->p1, aLine->p2)));
+  }
+  else {
+    std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(theEntity->entity());
+    if (anArc) {
+      double* u = theAuxParameters->point()->x;
+      double* diff = theAuxParameters->point()->y;
+      *u = (*anArc->startAngle + *anArc->endAngle) * 0.5;
+      *diff = (*anArc->endAngle - *anArc->startAngle) * 0.5;
+
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintCurveValue(*aPoint, aPoint->x, *anArc, u)));
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintCurveValue(*aPoint, aPoint->y, *anArc, u)));
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintDifference(anArc->startAngle, u, diff)));
+      aConstrList.push_back(GCSConstraintPtr(
+          new GCS::ConstraintDifference(u, anArc->endAngle, diff)));
+    }
+  }
 
-  return ConstraintWrapperPtr(
+  return aConstrList.empty() ? ConstraintWrapperPtr() : ConstraintWrapperPtr(
       new PlaneGCSSolver_ConstraintWrapper(aConstrList, CONSTRAINT_MIDDLE_POINT));
 }
 
index 9643b2aafc7a8f6e180b5ddb81ce2fdb3ec1d075..15b8dc285240300051b3f31c671043bd78a58a5b 100644 (file)
 
 #include <SketchSolver_ConstraintMiddle.h>
 #include <PlaneGCSSolver_ConstraintWrapper.h>
+#include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_Storage.h>
+#include <PlaneGCSSolver_Tools.h>
 #include <PlaneGCSSolver_UpdateCoincidence.h>
 
 void SketchSolver_ConstraintMiddle::getAttributes(
@@ -26,13 +30,55 @@ void SketchSolver_ConstraintMiddle::getAttributes(
     std::vector<EntityWrapperPtr>& theAttributes)
 {
   SketchSolver_Constraint::getAttributes(theValue, theAttributes);
+
+  // create auxiliary point if middle point on arc is specified
+  if (theAttributes[2]->type() == ENTITY_ARC) {
+    std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+        std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+    myOddPoint = GCSPointPtr(new GCS::Point);
+    myOddPoint->x = aStorage->createParameter();
+    myOddPoint->y = aStorage->createParameter();
+    theAttributes[1] = PointWrapperPtr(new PlaneGCSSolver_PointWrapper(myOddPoint));
+  }
+}
+
+bool SketchSolver_ConstraintMiddle::remove()
+{
+  if (myOddPoint) {
+    std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+        std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+    GCS::SET_pD aParams;
+    aParams.insert(myOddPoint->x);
+    aParams.insert(myOddPoint->y);
+    aStorage->removeParameters(aParams);
+  }
+  return SketchSolver_ConstraintCoincidence::remove();
 }
 
 void SketchSolver_ConstraintMiddle::notify(const FeaturePtr&      theFeature,
                                            PlaneGCSSolver_Update* theUpdater)
 {
-  if (theFeature == myBaseConstraint && myInSolver)
-    return; // the constraint is already being updated
+  if (theFeature == myBaseConstraint && myInSolver) {
+    // the constraint is already being updated,
+    // update the middle point parameter if the constraint is "point-on-arc".
+    if (myOddPoint) {
+      EntityWrapperPtr anArcEntity =
+          myAttributes.front()->type() == ENTITY_ARC ? myAttributes.front() : myAttributes.back();
+      EdgeWrapperPtr anArcEdge =
+          std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(anArcEntity);
+      std::shared_ptr<GCS::Arc> anArc;
+      if (anArcEdge)
+        anArc = std::dynamic_pointer_cast<GCS::Arc>(anArcEdge->entity());
+      if (anArc) {
+        // recalculate parameters of middle point according to arc
+        *myOddPoint->x = (*anArc->startAngle + *anArc->endAngle) * 0.5;
+        *myOddPoint->y = (*anArc->endAngle - *anArc->startAngle) * 0.5;
+      }
+    }
+    return;
+  }
 
   PlaneGCSSolver_UpdateCoincidence* anUpdater =
       static_cast<PlaneGCSSolver_UpdateCoincidence*>(theUpdater);
@@ -45,13 +91,8 @@ void SketchSolver_ConstraintMiddle::notify(const FeaturePtr&      theFeature,
         // remove previously adde constraint
         myStorage->removeConstraint(myBaseConstraint);
         // merge divided constraints into single object
-        std::list<GCSConstraintPtr> aGCSConstraints;
-        std::shared_ptr<PlaneGCSSolver_ConstraintWrapper> aConstraint =
-            std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(myMiddle);
-        aGCSConstraints.push_back(aConstraint->constraints().front());
-        aConstraint =
-            std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(mySolverConstraint);
-        aGCSConstraints.push_back(aConstraint->constraints().front());
+        std::list<GCSConstraintPtr> aGCSConstraints = myMiddle->constraints();
+        aGCSConstraints.push_front(mySolverConstraint->constraints().front());
 
         myMiddle = ConstraintWrapperPtr();
         mySolverConstraint = ConstraintWrapperPtr(
@@ -72,10 +113,11 @@ void SketchSolver_ConstraintMiddle::notify(const FeaturePtr&      theFeature,
           std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(mySolverConstraint);
       std::list<GCSConstraintPtr> aGCSConstraints = aConstraint->constraints();
 
-      myMiddle = ConstraintWrapperPtr(
-          new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints.front(), CONSTRAINT_MIDDLE_POINT));
       mySolverConstraint = ConstraintWrapperPtr(
-          new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints.back(), CONSTRAINT_MIDDLE_POINT));
+        new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints.front(), CONSTRAINT_MIDDLE_POINT));
+      aGCSConstraints.pop_front();
+      myMiddle = ConstraintWrapperPtr(
+          new PlaneGCSSolver_ConstraintWrapper(aGCSConstraints, CONSTRAINT_MIDDLE_POINT));
 
       // send middle constraint only
       myStorage->addConstraint(myBaseConstraint, myMiddle);
index 48e11560aebad9cd83b55bda5b00d2d012884fa8..3d63197857411bb8a203aee8876ba95b58bca5ee 100644 (file)
@@ -38,6 +38,9 @@ public:
   virtual void notify(const FeaturePtr&      theFeature,
                       PlaneGCSSolver_Update* theUpdater);
 
+  /// \brief Remove constraint
+  virtual bool remove();
+
 protected:
   /// \brief Generate list of attributes of constraint in order useful for constraints
   /// \param[out] theValue      numerical characteristic of constraint (e.g. distance)
@@ -47,6 +50,7 @@ protected:
 
 private:
   ConstraintWrapperPtr myMiddle;
+  GCSPointPtr myOddPoint; ///< auxiliary point to adjust midpoint-on-arc
 };
 
 #endif
index 5a66f99b006ed8b2f3764f0d53d404b6223737d2..84d701e2b2c10c9b1f7d77566a1c3a64638e5129 100644 (file)
@@ -179,6 +179,7 @@ static SolverConstraintPtr move(StoragePtr theStorage,
     SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
     if (aConstraint->error().empty()) {
       aConstraint->startPoint(theFrom);
+      theStorage->adjustParametrizationOfArcs();
       theSketchSolver->initialize();
       aConstraint->moveTo(theTo);
       theStorage->setNeedToResolve(true);
@@ -236,8 +237,10 @@ bool SketchSolver_Group::resolveConstraints()
 
     PlaneGCSSolver_Solver::SolveStatus aResult = PlaneGCSSolver_Solver::STATUS_OK;
     try {
-      if (!isGroupEmpty)
+      if (!isGroupEmpty) {
+        myStorage->adjustParametrizationOfArcs();
         aResult = mySketchSolver->solve();
+      }
       if (aResult == PlaneGCSSolver_Solver::STATUS_FAILED &&
           !myTempConstraints.empty()) {
         mySketchSolver->undo();
index 4d483105843642a9bff4be766c9ff31b0983c3c1..0757abfb7f2abf3f060ff3559a7fec4f09f70831 100644 (file)
@@ -150,6 +150,11 @@ public:
   /// \brief Notify all subscribers about update of the feature
   void notify(const FeaturePtr& theFeature) const;
 
+  /// \brief Make parametrization of arcs consistent.
+  ///        Forward arcs should have the last parameter greater than the first parameter.
+  ///        Reversed arcs should have the last parameter lesser than the first parameter.
+  virtual void adjustParametrizationOfArcs() = 0;
+
 protected:
   /// \brief Convert result to feature or attribute if theResult is linked to center of circle/arc
   static void resultToFeatureOrAttribute(const ObjectPtr& theResult,