TestConstraintHorizontalValidator.py
TestConstraintLength.py
TestConstraintMiddlePoint.py
+ TestConstraintMiddlePointOnArc.py
TestConstraintParallel.py
TestConstraintPerpendicular.py
TestConstraintPerpendicularArcLine.py
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;
}
}
--- /dev/null
+# 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"
</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">
PlaneGCSSolver_PointWrapper.h
PlaneGCSSolver_ScalarWrapper.h
PlaneGCSSolver_AngleWrapper.h
+ PlaneGCSSolver_BooleanWrapper.h
PlaneGCSSolver_Tools.h
)
PlaneGCSSolver_PointWrapper.cpp
PlaneGCSSolver_ScalarWrapper.cpp
PlaneGCSSolver_AngleWrapper.cpp
+ PlaneGCSSolver_BooleanWrapper.cpp
PlaneGCSSolver_Tools.cpp
)
#include <PlaneGCSSolver_AttributeBuilder.h>
#include <PlaneGCSSolver_PointWrapper.h>
#include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
#include <GeomDataAPI_Point2D.h>
#include <ModelAPI_AttributeDouble.h>
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)
{
aResult = createPoint(theAttribute, myStorage);
if (!aResult)
aResult = createScalar(theAttribute, myStorage);
+ if (!aResult)
+ aResult = createBoolean(theAttribute);
if (aResult && !myStorage)
aResult->setExternal(true);
return aResult;
--- /dev/null
+// 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)
+{
+}
--- /dev/null
+// 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
/// Types of entities
enum SketchSolver_EntityType {
ENTITY_UNKNOWN = 0,
+ ENTITY_BOOLEAN,
ENTITY_SCALAR,
ENTITY_ANGLE,
ENTITY_POINT,
#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;
#define PlaneGCSSolver_EdgeWrapper_H_
#include <PlaneGCSSolver_Defs.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
#include <PlaneGCSSolver_EntityWrapper.h>
/**
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;
#include <PlaneGCSSolver_EdgeWrapper.h>
#include <PlaneGCSSolver_PointWrapper.h>
#include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
#include <SketchPlugin_Arc.h>
#include <SketchPlugin_Circle.h>
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
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)
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() ||
#include <PlaneGCSSolver_Storage.h>
#include <PlaneGCSSolver_Solver.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
#include <PlaneGCSSolver_ConstraintWrapper.h>
#include <PlaneGCSSolver_EdgeWrapper.h>
#include <PlaneGCSSolver_PointWrapper.h>
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());
+ }
}
}
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
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;
}
// 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));
}
}
+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)
{
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,
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);
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);
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));
}
#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(
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);
// 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(
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);
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)
private:
ConstraintWrapperPtr myMiddle;
+ GCSPointPtr myOddPoint; ///< auxiliary point to adjust midpoint-on-arc
};
#endif
SolverConstraintPtr(aConstraint)->process(theStorage, theEventsBlocked);
if (aConstraint->error().empty()) {
aConstraint->startPoint(theFrom);
+ theStorage->adjustParametrizationOfArcs();
theSketchSolver->initialize();
aConstraint->moveTo(theTo);
theStorage->setNeedToResolve(true);
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();
/// \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,