Salome HOME
Task 2.12. New entities: ellipses and arcs of ellipses (issue #3003)
authorazv <azv@opencascade.com>
Thu, 3 Oct 2019 11:53:38 +0000 (14:53 +0300)
committerazv <azv@opencascade.com>
Thu, 3 Oct 2019 11:53:38 +0000 (14:53 +0300)
Constraint Perpendicular for elliptic arc and line

src/SketchSolver/CMakeLists.txt
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/SketchSolver_ConstraintPerpendicular.cpp [new file with mode: 0644]
src/SketchSolver/SketchSolver_ConstraintPerpendicular.h [new file with mode: 0644]

index a543a7014786c729ba231a28ad41b4f992562506..fafa0372ac5780f3b4d4905d4985d8a3f2498eca 100644 (file)
@@ -50,6 +50,7 @@ SET(SKETCHSOLVER_CONSTRAINT_HEADERS
     SketchSolver_ConstraintMiddle.h
     SketchSolver_ConstraintMirror.h
     SketchSolver_ConstraintFixed.h
+    SketchSolver_ConstraintPerpendicular.h
     SketchSolver_ConstraintTangent.h
     SketchSolver_ConstraintMulti.h
     SketchSolver_ConstraintMultiRotation.h
@@ -74,6 +75,7 @@ SET(SKETCHSOLVER_CONSTRAINT_SOURCES
     SketchSolver_ConstraintMiddle.cpp
     SketchSolver_ConstraintMirror.cpp
     SketchSolver_ConstraintFixed.cpp
+    SketchSolver_ConstraintPerpendicular.cpp
     SketchSolver_ConstraintTangent.cpp
     SketchSolver_ConstraintMulti.cpp
     SketchSolver_ConstraintMultiRotation.cpp
index fc543f5b053b8cd25ab0185437a480c344ab85c5..0f46e7544d73920b8f3349618b3f1a6dc7c4d982 100644 (file)
@@ -74,6 +74,7 @@ enum SketchSolver_ConstraintType {
   CONSTRAINT_VERTICAL,
   CONSTRAINT_PARALLEL,
   CONSTRAINT_PERPENDICULAR,
+  CONSTRAINT_PERPENDICULAR_CURVES,
   CONSTRAINT_SYMMETRIC,
   CONSTRAINT_EQUAL,           // base equality if we don't know the measured objects yet
   CONSTRAINT_EQUAL_LINES,
index ccc07b05b128905710d2e2b264f15649fdc61445..a13a7b891b5e8f0a498f52edb457e64ba9d7f7cf 100644 (file)
@@ -34,6 +34,7 @@
 #include <SketchSolver_ConstraintLength.h>
 #include <SketchSolver_ConstraintMiddle.h>
 #include <SketchSolver_ConstraintMirror.h>
+#include <SketchSolver_ConstraintPerpendicular.h>
 #include <SketchSolver_ConstraintTangent.h>
 #include <SketchSolver_ConstraintMultiRotation.h>
 #include <SketchSolver_ConstraintMultiTranslation.h>
@@ -49,6 +50,7 @@
 #include <SketchPlugin_ConstraintMiddle.h>
 #include <SketchPlugin_ConstraintMirror.h>
 #include <SketchPlugin_ConstraintRigid.h>
+#include <SketchPlugin_ConstraintPerpendicular.h>
 #include <SketchPlugin_ConstraintTangent.h>
 #include <SketchPlugin_Line.h>
 #include <SketchPlugin_MultiRotation.h>
@@ -119,6 +121,11 @@ static ConstraintWrapperPtr
   createConstraintMiddlePoint(std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
                               std::shared_ptr<PlaneGCSSolver_EdgeWrapper>  theEntity,
                               std::shared_ptr<PlaneGCSSolver_PointWrapper> theAuxParameters);
+static ConstraintWrapperPtr
+  createConstraintAngleBetweenCurves(std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue,
+                                     std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
+                                     std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
+                                     std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2);
 
 static GCS::SET_pD scalarParameters(const ScalarWrapperPtr& theScalar);
 static GCS::SET_pD pointParameters(const PointWrapperPtr& thePoint);
@@ -163,6 +170,8 @@ SolverConstraintPtr PlaneGCSSolver_Tools::createConstraint(ConstraintPtr theCons
     return SolverConstraintPtr(new SketchSolver_ConstraintMultiRotation(theConstraint));
   } else if (theConstraint->getKind() == SketchPlugin_ConstraintAngle::ID()) {
     return SolverConstraintPtr(new SketchSolver_ConstraintAngle(theConstraint));
+  } else if (theConstraint->getKind() == SketchPlugin_ConstraintPerpendicular::ID()) {
+    return SolverConstraintPtr(new SketchSolver_ConstraintPerpendicular(theConstraint));
   }
   // All other types of constraints
   return SolverConstraintPtr(new SketchSolver_Constraint(theConstraint));
@@ -243,6 +252,10 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
   case CONSTRAINT_PERPENDICULAR:
     aResult = createConstraintPerpendicular(anEntity1, GCS_EDGE_WRAPPER(theEntity2));
     break;
+  case CONSTRAINT_PERPENDICULAR_CURVES:
+    aResult = createConstraintAngleBetweenCurves(GCS_SCALAR_WRAPPER(theValue),
+                  aPoint1, anEntity1, GCS_EDGE_WRAPPER(theEntity2));
+    break;
   case CONSTRAINT_EQUAL_LINES:
   case CONSTRAINT_EQUAL_ELLIPSES:
     anIntermediate = GCS_SCALAR_WRAPPER(theValue); // parameter is used to store length of lines
@@ -663,6 +676,18 @@ ConstraintWrapperPtr createConstraintPerpendicular(
       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_PERPENDICULAR));
 }
 
+ConstraintWrapperPtr createConstraintAngleBetweenCurves(
+    std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue,
+    std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
+    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
+    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2)
+{
+  GCSConstraintPtr aNewConstr(new GCS::ConstraintAngleViaPoint(
+      *theEntity1->entity(), *theEntity2->entity(), *thePoint->point(), theValue->scalar()));
+  return ConstraintWrapperPtr(
+      new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_PERPENDICULAR_CURVES));
+}
+
 ConstraintWrapperPtr createConstraintEqual(
     const SketchSolver_ConstraintType& theType,
     std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
diff --git a/src/SketchSolver/SketchSolver_ConstraintPerpendicular.cpp b/src/SketchSolver/SketchSolver_ConstraintPerpendicular.cpp
new file mode 100644 (file)
index 0000000..4109bb5
--- /dev/null
@@ -0,0 +1,411 @@
+// 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 <SketchSolver_ConstraintPerpendicular.h>
+#include <SketchSolver_Error.h>
+
+#include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_Storage.h>
+#include <PlaneGCSSolver_Tools.h>
+#include <PlaneGCSSolver_UpdateCoincidence.h>
+
+#include <GeomAPI_Circ2d.h>
+#include <GeomAPI_Lin2d.h>
+#include <GeomAPI_Pnt2d.h>
+#include <GeomAPI_Ellipse2d.h>
+
+#include <SketchPlugin_Arc.h>
+#include <SketchPlugin_Circle.h>
+#include <SketchPlugin_ConstraintCoincidence.h>
+#include <SketchPlugin_ConstraintMiddle.h>
+
+#include <cmath>
+
+#define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
+#define GCS_POINT_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(x)
+
+/// \brief Obtain constrained features
+static void getFeatures(const ConstraintPtr& theConstraint,
+                        FeaturePtr& theFeature1,
+                        FeaturePtr& theFeature2);
+
+/// \brief Check whether the entities has only one shared point or less.
+///        Return list of coincident points.
+static std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1,
+                                                       FeaturePtr theFeature2);
+
+/// \brief Collect points coincident with each of two features
+static std::set<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2);
+
+static void calculateIntersectionPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2,
+                                       GCSPointPtr& theIntersectionPoint);
+
+/// sets angle to 90 or -90 degrees
+static void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
+                                     const GCSCurvePtr& theCurve2,
+                                     const GCSPointPtr& thePoint,
+                                     double*            theAngle);
+
+
+void SketchSolver_ConstraintPerpendicular::process()
+{
+  cleanErrorMsg();
+  if (!myBaseConstraint || !myStorage) {
+    // Not enough parameters are assigned
+    return;
+  }
+
+  EntityWrapperPtr aValue;
+  std::vector<EntityWrapperPtr> anAttributes;
+  SketchSolver_Constraint::getAttributes(aValue, anAttributes);
+  if (!myErrorMsg.empty())
+    return;
+
+  rebuild();
+  if (!myErrorMsg.empty())
+    return;
+
+  myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
+}
+
+void SketchSolver_ConstraintPerpendicular::rebuild()
+{
+  if (mySolverConstraint)
+    myStorage->removeConstraint(myBaseConstraint);
+
+  std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+      std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+  mySolverConstraint = ConstraintWrapperPtr();
+  mySharedPoint = AttributePtr();
+  if (myAuxPoint) {
+    GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
+    aStorage->removeParameters(aParams);
+    myAuxPoint = EntityWrapperPtr();
+  }
+
+  // Check the quantity of entities of each type and their order (arcs first)
+  int aNbLines = 0;
+  int aNbCircles = 0;
+  int aNbOther = 0;
+  std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
+  for (; anEntIt != myAttributes.end(); ++anEntIt) {
+    if (!(*anEntIt).get())
+      continue;
+    if ((*anEntIt)->type() == ENTITY_LINE)
+      ++aNbLines;
+    else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE)
+      ++aNbCircles;
+    else if ((*anEntIt)->type() != ENTITY_POINT)
+      ++aNbOther;
+  }
+
+  if (aNbLines + aNbCircles + aNbOther == 2) {
+    if (aNbOther > 0)
+      myType = CONSTRAINT_PERPENDICULAR_CURVES;
+  }
+  else {
+    myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
+    return;
+  }
+
+  FeaturePtr aFeature1, aFeature2;
+  getFeatures(myBaseConstraint, aFeature1, aFeature2);
+
+  // check number of coincident points
+  std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
+  // Try to find non-boundary points coincident with both features.
+  // It is necesasry to create perpendicularity with ellipse
+  if (aCoincidentPoints.empty() && aNbOther > 0)
+    aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
+
+  EntityWrapperPtr aSharedPointEntity;
+  std::list<GCSConstraintPtr> anAuxConstraints;
+  if (!aCoincidentPoints.empty()) {
+    mySharedPoint = *aCoincidentPoints.begin();
+    aSharedPointEntity = myStorage->entity(mySharedPoint);
+  }
+  else if (aNbOther > 0) {
+    // create auxiliary point
+    GCSPointPtr aPoint(new GCS::Point);
+    aPoint->x = aStorage->createParameter();
+    aPoint->y = aStorage->createParameter();
+    calculateIntersectionPoint(myAttributes.front(), myAttributes.back(), aPoint);
+
+    myAuxPoint.reset(new PlaneGCSSolver_PointWrapper(aPoint));
+    aSharedPointEntity = myAuxPoint;
+
+    // create auxiliary coincident constraints for tangency with ellipse
+    EntityWrapperPtr aDummy;
+    ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
+        CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.front(), aDummy);
+    anAuxConstraints = aCoincidence->constraints();
+    aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
+        CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.back(), aDummy);
+    anAuxConstraints.insert(anAuxConstraints.end(),
+        aCoincidence->constraints().begin(), aCoincidence->constraints().end());
+  }
+
+  ScalarWrapperPtr anAngleWrapper;
+  if (aSharedPointEntity) {
+    adjustAngleBetweenCurves(GCS_EDGE_WRAPPER(myAttributes.front())->entity(),
+                             GCS_EDGE_WRAPPER(myAttributes.back())->entity(),
+                             GCS_POINT_WRAPPER(aSharedPointEntity)->point(), &myCurveCurveAngle);
+    anAngleWrapper.reset(new PlaneGCSSolver_ScalarWrapper(&myCurveCurveAngle));
+  }
+
+  mySolverConstraint = PlaneGCSSolver_Tools::createConstraint(myBaseConstraint, myType,
+      anAngleWrapper, aSharedPointEntity, EntityWrapperPtr(),
+      myAttributes.front(), myAttributes.back());
+
+  if (!anAuxConstraints.empty()) {
+    anAuxConstraints.insert(anAuxConstraints.end(), mySolverConstraint->constraints().begin(),
+        mySolverConstraint->constraints().end());
+    mySolverConstraint->setConstraints(anAuxConstraints);
+  }
+
+  myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
+}
+
+void SketchSolver_ConstraintPerpendicular::notify(const FeaturePtr&      theFeature,
+                                                  PlaneGCSSolver_Update* theUpdater)
+{
+  if (theFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
+    return;
+
+  // base constraint may become invalid (for example, during undo)
+  if (!myBaseConstraint->data() || !myBaseConstraint->data()->isValid())
+    return;
+
+  FeaturePtr aTgFeat1, aTgFeat2;
+  getFeatures(myBaseConstraint, aTgFeat1, aTgFeat2);
+
+  bool isRebuild = false;
+  if (theFeature->data() && theFeature->data()->isValid()) {
+    // the constraint has been created
+    if (!mySharedPoint) {
+      // features has no shared point, check whether coincidence constraint binds these features)
+      int aNbCoincidentFeatures = 0;
+      for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+        AttributeRefAttrPtr aRefAttr = theFeature->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+        if (!aRefAttr)
+          continue;
+
+        ObjectPtr anObj;
+        if (aRefAttr->isObject())
+          anObj = aRefAttr->object();
+        else
+          anObj = aRefAttr->attr()->owner();
+
+        FeaturePtr aFeature = ModelAPI_Feature::feature(anObj);
+        if (aFeature == aTgFeat1 || aFeature == aTgFeat2)
+          ++aNbCoincidentFeatures;
+      }
+
+      if (aNbCoincidentFeatures == 2)
+        isRebuild = true;
+    }
+  }
+
+  if (!isRebuild) {
+    if (mySharedPoint) {
+      // The features are tangent in the shared point, but the coincidence has been removed/updated.
+      // Check if the coincidence is the same.
+      std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2);
+      isRebuild = true;
+      std::set<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
+      for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
+        if (*anIt == mySharedPoint)
+          isRebuild = false; // the coincidence is still exists => nothing to change
+    }
+    else {
+      // check both features have a coincident point
+      std::set<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
+      isRebuild = (bool)(myAuxPoint.get()) == (!aCoincidentPoints.empty());
+    }
+  }
+
+  if (isRebuild)
+    rebuild();
+}
+
+
+
+
+// ==================   Auxiliary functions   =================================
+void getFeatures(const ConstraintPtr& theConstraint,
+                 FeaturePtr&          theFeature1,
+                 FeaturePtr&          theFeature2)
+{
+  AttributeRefAttrPtr aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
+  theFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
+  aRefAttr = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
+  theFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
+}
+
+static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+  const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
+  const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
+
+  std::set<FeaturePtr> aCoincidences;
+  std::set<AttributePtr>::const_iterator anIt;
+
+  // collect coincidences referred to the first feature
+  for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
+    FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
+    if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
+      aCoincidences.insert(aRef);
+  }
+
+  // leave only coincidences referred to the second feature
+  std::set<FeaturePtr> aCoincidencesBetweenFeatures;
+  for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
+    FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
+    if (aCoincidences.find(aRef) != aCoincidences.end())
+      aCoincidencesBetweenFeatures.insert(aRef);
+  }
+
+  return aCoincidencesBetweenFeatures;
+}
+
+std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+  std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
+  // collect points only
+  std::set<AttributePtr> aCoincidentPoints;
+  std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
+  for (; aCIt != aCoincidences.end(); ++ aCIt) {
+    AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
+    AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
+    if (!aRefAttrA || aRefAttrA->isObject() ||
+        !aRefAttrB || aRefAttrB->isObject())
+      continue;
+
+    AttributePtr anAttrA = aRefAttrA->attr();
+    AttributePtr anAttrB = aRefAttrB->attr();
+    if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
+        anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
+        anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
+        anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
+      aCoincidentPoints.insert(anAttrA);
+      aCoincidentPoints.insert(anAttrB);
+    }
+  }
+  return aCoincidentPoints;
+}
+
+static std::set<AttributePtr> refsToFeatureAndResults(FeaturePtr theFeature)
+{
+  std::set<AttributePtr> aRefs = theFeature->data()->refsToMe();
+  const std::list<ResultPtr>& aResults = theFeature->results();
+  for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
+      anIt != aResults.end(); ++anIt) {
+    const std::set<AttributePtr>& aResRefs = (*anIt)->data()->refsToMe();
+    aRefs.insert(aResRefs.begin(), aResRefs.end());
+  }
+  return aRefs;
+}
+
+// collect all points coincident with the feature
+static std::set<AttributePtr> pointsOnFeature(FeaturePtr theFeature)
+{
+  std::set<AttributePtr> aPoints;
+
+  std::set<AttributePtr> aRefs = refsToFeatureAndResults(theFeature);
+  for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
+    FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
+    if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+                 aRef->getKind() == SketchPlugin_ConstraintMiddle::ID())) {
+      for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+        AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+        if (aRefAttr) {
+          AttributePtr anAttr = aRefAttr->attr();
+          if (anAttr && anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
+                        anAttr->id() != SketchPlugin_Circle::CENTER_ID())
+            aPoints.insert(anAttr);
+        }
+      }
+    }
+  }
+  return aPoints;
+}
+
+std::set<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+  std::set<AttributePtr> aPointsOnF1 = pointsOnFeature(theFeature1);
+  std::set<AttributePtr> aPointsOnF2 = pointsOnFeature(theFeature2);
+
+  std::set<AttributePtr> aCommonPoints;
+  for (std::set<AttributePtr>::iterator anIt = aPointsOnF1.begin();
+       anIt != aPointsOnF1.end(); ++anIt)
+    if (aPointsOnF2.find(*anIt) != aPointsOnF2.end())
+      aCommonPoints.insert(*anIt);
+  return aCommonPoints;
+}
+
+void adjustAngleBetweenCurves(const GCSCurvePtr& theCurve1,
+                              const GCSCurvePtr& theCurve2,
+                              const GCSPointPtr& thePoint,
+                              double*            theAngle)
+{
+  double anAngle = GCS::System().calculateAngleViaPoint(*theCurve1, *theCurve2, *thePoint);
+  // bring angle to [-pi..pi]
+  if (anAngle >  PI) anAngle -= 2.0 * PI;
+  if (anAngle < -PI) anAngle += 2.0 * PI;
+  // set angle value according to the current angle between curves
+  if (anAngle >= 0)
+    *theAngle = PI / 2.;
+  else
+    *theAngle = -PI / 2.;
+}
+
+void calculateIntersectionPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2,
+                                GCSPointPtr& theIntersectionPoint)
+{
+  std::shared_ptr<GeomAPI_Ellipse2d> anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve1);
+  EntityWrapperPtr aCurve2 = theCurve2;
+  if (!anEllipse) {
+    // try converting to ellipse the second curve
+    anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve2);
+    if (!anEllipse)
+      return; // no one curve is ellipse
+    aCurve2 = theCurve1;
+  }
+
+  GeomPnt2dPtr aP1, aP2;
+  if (aCurve2->type() == ENTITY_LINE) {
+    std::shared_ptr<GeomAPI_Lin2d> aLine = PlaneGCSSolver_Tools::line(aCurve2);
+    anEllipse->distance(aLine, aP1, aP2);
+  }
+  else if (aCurve2->type() == ENTITY_ARC || aCurve2->type() == ENTITY_CIRCLE) {
+    std::shared_ptr<GeomAPI_Circ2d> aCircle = PlaneGCSSolver_Tools::circle(aCurve2);
+    anEllipse->distance(aCircle, aP1, aP2);
+  }
+  else if (aCurve2->type() == ENTITY_ELLIPSE || aCurve2->type() == ENTITY_ELLIPTIC_ARC) {
+    std::shared_ptr<GeomAPI_Ellipse2d> anEl2 = PlaneGCSSolver_Tools::ellipse(aCurve2);
+    anEllipse->distance(anEl2, aP1, aP2);
+  }
+
+  if (aP1 && aP2) {
+    *theIntersectionPoint->x = 0.5 * (aP1->x() + aP2->x());
+    *theIntersectionPoint->y = 0.5 * (aP1->y() + aP2->y());
+  }
+}
diff --git a/src/SketchSolver/SketchSolver_ConstraintPerpendicular.h b/src/SketchSolver/SketchSolver_ConstraintPerpendicular.h
new file mode 100644 (file)
index 0000000..822c702
--- /dev/null
@@ -0,0 +1,55 @@
+// 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 SketchSolver_ConstraintPerpendicular_H_
+#define SketchSolver_ConstraintPerpendicular_H_
+
+#include <SketchSolver_Constraint.h>
+
+/** \class   SketchSolver_ConstraintPerpendicular
+ *  \ingroup Plugins
+ *  \brief   Convert perpendicularity constraint to SolveSpace structure
+ */
+class SketchSolver_ConstraintPerpendicular : public SketchSolver_Constraint
+{
+public:
+  /// Constructor based on SketchPlugin constraint
+  SketchSolver_ConstraintPerpendicular(ConstraintPtr theConstraint) :
+      SketchSolver_Constraint(theConstraint),
+      myCurveCurveAngle(0.0)
+  {}
+
+  /// \brief Notify this object about the feature is changed somewhere
+  virtual void notify(const FeaturePtr&      theFeature,
+                      PlaneGCSSolver_Update* theUpdater);
+
+protected:
+  /// \brief Converts SketchPlugin constraint to a list of solver constraints
+  virtual void process();
+
+  /// \brief Remove current constraint from the storage and build is again
+  void rebuild();
+
+private:
+  double myCurveCurveAngle;
+  AttributePtr mySharedPoint;
+  EntityWrapperPtr myAuxPoint;
+};
+
+#endif