From 1655cad439fcef1f104340306aaed349d5b197ef Mon Sep 17 00:00:00 2001 From: azv Date: Thu, 9 Mar 2017 15:24:00 +0300 Subject: [PATCH] Implement Collinear constraint --- src/PythonAPI/model/sketcher/tools.py | 6 +- src/SketchPlugin/CMakeLists.txt | 2 +- .../Test/TestConstraintCollinear.py | 221 +++++++++++------- src/SketchPlugin/plugin-Sketch.xml | 2 - .../PlaneGCSSolver/PlaneGCSSolver_Tools.cpp | 25 -- .../PlaneGCSSolver_UpdateCoincidence.cpp | 60 ++++- .../PlaneGCSSolver_UpdateCoincidence.h | 8 + .../SketchSolver_ConstraintCoincidence.h | 2 +- .../SketchSolver_ConstraintCollinear.cpp | 159 +++++++++---- .../SketchSolver_ConstraintCollinear.h | 21 +- 10 files changed, 344 insertions(+), 162 deletions(-) diff --git a/src/PythonAPI/model/sketcher/tools.py b/src/PythonAPI/model/sketcher/tools.py index 31efc0f28..3603f2006 100644 --- a/src/PythonAPI/model/sketcher/tools.py +++ b/src/PythonAPI/model/sketcher/tools.py @@ -1,6 +1,7 @@ # Author: Sergey Pokhodenko # Copyright (C) 2014-20xx CEA/DEN, EDF R&D +import ModelHighAPI def addPolyline(sketch, *coords): """Add a poly-line to sketch. @@ -44,4 +45,7 @@ def addPolygon(sketch, *coords): def dof(sketch): """ Extract degrees of freedom for the given sketch """ - return int(filter(str.isdigit, sketch.string("SolverDOF").value())) \ No newline at end of file + aSketch = sketch + if issubclass(type(aSketch), ModelHighAPI.ModelHighAPI_Interface): + aSketch = sketch.feature() + return int(filter(str.isdigit, aSketch.string("SolverDOF").value())) \ No newline at end of file diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 152a8fe18..d7fb311d3 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -121,7 +121,7 @@ INSTALL(FILES ${TEXT_RESOURCES} DESTINATION ${SHAPER_INSTALL_XML_RESOURCES}) ADD_UNIT_TESTS(TestSketchPointLine.py TestSketchArcCircle.py TestConstraintCoincidence.py - # TestConstraintCollinear.py + TestConstraintCollinear.py TestConstraintLength.py TestConstraintDistance.py TestConstraintParallel.py diff --git a/src/SketchPlugin/Test/TestConstraintCollinear.py b/src/SketchPlugin/Test/TestConstraintCollinear.py index 8097c8e62..8e4dfdcf8 100644 --- a/src/SketchPlugin/Test/TestConstraintCollinear.py +++ b/src/SketchPlugin/Test/TestConstraintCollinear.py @@ -8,102 +8,165 @@ data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId()); """ + +#========================================================================= +# Initialization of the test +#========================================================================= + from GeomDataAPI import * from ModelAPI import * import math +import unittest from salome.shaper import model -#========================================================================= -# Initialization of the test -#========================================================================= +__updated__ = "2017-03-06" -__updated__ = "2016-01-28" +class TestConstraintCollinear(unittest.TestCase): + def setUp(self): + model.begin() + self.myDocument = model.moduleDocument() + self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY")) + self.myTolerance = 1.e-6 + self.myDOF = 0 + def tearDown(self): + model.end() + assert(model.checkPythonDump()) -def checkCollinearVec(theX1, theY1, theX2, theY2): - TOL = 2.e-6 + def checkVectorCollinearity(self, theX1, theY1, theX2, theY2): aLen1 = math.hypot(theX1, theY1) aLen2 = math.hypot(theX2, theY2) aDot = theX1 * theX2 + theY1 * theY2 - assert (math.fabs(math.fabs(aDot) - aLen1 * aLen2) < TOL**2) - -def checkCollinear(theLine1, theLine2): - aStartPoint1 = geomDataAPI_Point2D(theLine1.attribute("StartPoint")) - aEndPoint1 = geomDataAPI_Point2D(theLine1.attribute("EndPoint")) - aStartPoint2 = geomDataAPI_Point2D(theLine2.attribute("StartPoint")) - aEndPoint2 = geomDataAPI_Point2D(theLine2.attribute("EndPoint")) - + self.assertTrue(math.fabs(math.fabs(aDot) - aLen1 * aLen2) < self.myTolerance**2, "Vectors ({0}, {1}) and ({2}, {3}) do not collinear".format(theX1, theY1, theX2, theY2)) + + def checkLineCollinearity(self, theLine1, theLine2): + aStartPoint1 = theLine1.startPoint() + aEndPoint1 = theLine1.endPoint() + aStartPoint2 = theLine2.startPoint() + aEndPoint2 = theLine2.endPoint() + aDir1x, aDir1y = aEndPoint1.x() - aStartPoint1.x(), aEndPoint1.y() - aStartPoint1.y() aDir2x, aDir2y = aEndPoint2.x() - aStartPoint1.x(), aEndPoint2.y() - aStartPoint1.y() aDir3x, aDir3y = aStartPoint2.x() - aStartPoint1.x(), aStartPoint2.y() - aStartPoint1.y() - checkCollinearVec(aDir1x, aDir1y, aDir2x, aDir2y) - checkCollinearVec(aDir1x, aDir1y, aDir3x, aDir3y) + self.checkVectorCollinearity(aDir1x, aDir1y, aDir2x, aDir2y) + self.checkVectorCollinearity(aDir1x, aDir1y, aDir3x, aDir3y) + def moveLineAndCheckCollinearity(self, theLine1, theLine2): + deltaX = deltaY = 10. + theLine1.startPoint().setValue(theLine1.startPoint().x() + deltaX, theLine1.startPoint().y() + deltaY) + model.do() + self.checkLineCollinearity(theLine1, theLine2) + self.assertEqual(model.dof(self.mySketch), self.myDOF) -aSession = ModelAPI_Session.get() -aDocument = aSession.moduleDocument() -#========================================================================= -# Creation of a sketch -#========================================================================= -aSession.startOperation() -aSketchCommonFeature = aDocument.addFeature("Sketch") -aSketchFeature = featureToCompositeFeature(aSketchCommonFeature) -origin = geomDataAPI_Point(aSketchFeature.attribute("Origin")) -origin.setValue(0, 0, 0) -dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX")) -dirx.setValue(1, 0, 0) -norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm")) -norm.setValue(0, 0, 1) -aSession.finishOperation() -#========================================================================= -# Create two lines -#========================================================================= -aSession.startOperation() -# line A -aSketchLineA = aSketchFeature.addFeature("SketchLine") -aLineAStartPoint = geomDataAPI_Point2D(aSketchLineA.attribute("StartPoint")) -aLineAEndPoint = geomDataAPI_Point2D(aSketchLineA.attribute("EndPoint")) -aSketchLineB = aSketchFeature.addFeature("SketchLine") -aLineAStartPoint.setValue(0., 25) -aLineAEndPoint.setValue(85., 25) -# line B -aLineBStartPoint = geomDataAPI_Point2D(aSketchLineB.attribute("StartPoint")) -aLineBEndPoint = geomDataAPI_Point2D(aSketchLineB.attribute("EndPoint")) -aLineBStartPoint.setValue(0., 50) -aLineBEndPoint.setValue(80., 75) -aSession.finishOperation() -assert (model.dof(aSketchFeature) == 8) -#========================================================================= -# Link lines with collinear constraint -#========================================================================= -aSession.startOperation() -aParallelConstraint = aSketchFeature.addFeature("SketchConstraintCollinear") -refattrA = aParallelConstraint.refattr("ConstraintEntityA") -refattrB = aParallelConstraint.refattr("ConstraintEntityB") -refattrA.setObject(aSketchLineA.firstResult()) -refattrB.setObject(aSketchLineB.firstResult()) -aParallelConstraint.execute() -aSession.finishOperation() -checkCollinear(aSketchLineA, aSketchLineB) -assert (model.dof(aSketchFeature) == 6) -#========================================================================= -# Check values and move one constrainted object -#========================================================================= -deltaX = deltaY = 10. -# rotate line, check that reference's line points are moved also -aLineBStartPointPrev = (aLineBStartPoint.x(), aLineBStartPoint.y()) -aLineBEndPointPrev = (aLineBEndPoint.x(), aLineBEndPoint.y()) -aSession.startOperation() -aLineAStartPoint.setValue(aLineAStartPoint.x() + deltaX, - aLineAStartPoint.y() + deltaY) -aLineAEndPoint.setValue(aLineAEndPoint.x() - deltaX, - aLineAEndPoint.y() - deltaY) -aSession.finishOperation() -checkCollinear(aSketchLineA, aSketchLineB) -assert (model.dof(aSketchFeature) == 6) + theLine1.endPoint().setValue(theLine1.endPoint().x() - deltaX, theLine1.endPoint().y() - deltaY) + model.do() + self.checkLineCollinearity(theLine1, theLine2) + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def test_collinear_base(self): + """ Test 1. Collinearity two independent lines + """ + aSketchLineA = self.mySketch.addLine(0., 25., 85., 25.) + aSketchLineB = self.mySketch.addLine(0., 50., 80., 75.) + self.myDOF += 8 + self.assertEqual(model.dof(self.mySketch), self.myDOF) + self.mySketch.setCollinear(aSketchLineA, aSketchLineB) + self.myDOF -= 2 + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + self.checkLineCollinearity(aSketchLineA, aSketchLineB) + self.moveLineAndCheckCollinearity(aSketchLineA, aSketchLineB) + + def test_collinear_connected_lines(self): + """ Test 2. Collinearity of two lines in polyline + """ + aSketchLineA = self.mySketch.addLine(10., 20., 30., 40.) + aSketchLineB = self.mySketch.addLine(30., 40., 30., 70.) + self.myDOF += 8 + self.assertEqual(model.dof(self.mySketch), self.myDOF) + self.mySketch.setCoincident(aSketchLineA.endPoint(), aSketchLineB.startPoint()) + self.myDOF -= 2 + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + self.mySketch.setCollinear(aSketchLineA, aSketchLineB) + self.myDOF -= 1 + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + self.checkLineCollinearity(aSketchLineA, aSketchLineB) + self.moveLineAndCheckCollinearity(aSketchLineA, aSketchLineB) + + def test_collinear_point_on_line(self): + """ Test 3. Collinearity for line which extremity is coincident with other line + """ + aSketchLineA = self.mySketch.addLine(10., 20., 30., 40.) + aSketchLineB = self.mySketch.addLine(20., 40., 30., 70.) + self.myDOF += 8 + self.assertEqual(model.dof(self.mySketch), self.myDOF) + self.mySketch.setCoincident(aSketchLineA.result(), aSketchLineB.startPoint()) + self.myDOF -= 1 + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + self.mySketch.setCollinear(aSketchLineA, aSketchLineB) + self.myDOF -= 1 + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + self.checkLineCollinearity(aSketchLineA, aSketchLineB) + self.moveLineAndCheckCollinearity(aSketchLineA, aSketchLineB) + + def test_already_collinear(self): + """ Test 4. Collinearity two lines connected by extremities to each other + """ + aSketchLineA = self.mySketch.addLine(10., 20., 40., 50.) + aSketchLineB = self.mySketch.addLine(20., 40., 40., 70.) + self.myDOF += 8 + self.assertEqual(model.dof(self.mySketch), self.myDOF) + self.mySketch.setCoincident(aSketchLineA.result(), aSketchLineB.startPoint()) + self.mySketch.setCoincident(aSketchLineA.endPoint(), aSketchLineB.result()) + self.myDOF -= 2 + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + aCollinear = self.mySketch.setCollinear(aSketchLineA, aSketchLineB) + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + # check error message and remove non valid constraint + self.assertNotEqual(self.mySketch.solverError().value(), "") + self.myDocument.removeFeature(aCollinear.feature()) + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + def test_already_collinear2(self): + """ Test 5. Collinearity of two lines when one line is fully on other + """ + aSketchLineA = self.mySketch.addLine(10., 20., 30., 40.) + aSketchLineB = self.mySketch.addLine(20., 40., 30., 70.) + self.myDOF += 8 + self.assertEqual(model.dof(self.mySketch), self.myDOF) + self.mySketch.setCoincident(aSketchLineA.result(), aSketchLineB.startPoint()) + self.mySketch.setCoincident(aSketchLineA.result(), aSketchLineB.endPoint()) + self.myDOF -= 2 + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + aCollinear = self.mySketch.setCollinear(aSketchLineA, aSketchLineB) + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + + # check error message and remove non valid constraint + self.assertNotEqual(self.mySketch.solverError().value(), "") + self.myDocument.removeFeature(aCollinear.feature()) + model.do() + self.assertEqual(model.dof(self.mySketch), self.myDOF) + +if __name__ == '__main__': + unittest.main() #========================================================================= # End of test #========================================================================= - -assert(model.checkPythonDump()) diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index 5a62d1200..b72f3d8d3 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -540,7 +540,6 @@ - diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp index fcac0cb47..9dc93f371 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp @@ -88,10 +88,6 @@ static ConstraintWrapperPtr createConstraintTangent(const SketchSolver_ConstraintType& theType, std::shared_ptr theEntity1, std::shared_ptr theEntity2); -static ConstraintWrapperPtr - createConstraintCollinear(ConstraintPtr theConstraint, - std::shared_ptr theEntity1, - std::shared_ptr theEntity2); static ConstraintWrapperPtr createConstraintMiddlePoint(std::shared_ptr thePoint, std::shared_ptr theEntity); @@ -210,10 +206,6 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint( GCS_ENTITY_WRAPPER(theEntity1), GCS_ENTITY_WRAPPER(theEntity2)); break; - case CONSTRAINT_COLLINEAR: - aResult = createConstraintCollinear(theConstraint, - GCS_ENTITY_WRAPPER(theEntity1), GCS_ENTITY_WRAPPER(theEntity2)); - break; case CONSTRAINT_MULTI_TRANSLATION: case CONSTRAINT_MULTI_ROTATION: case CONSTRAINT_SYMMETRIC: @@ -411,23 +403,6 @@ ConstraintWrapperPtr createConstraintHorizVert( return ConstraintWrapperPtr(new PlaneGCSSolver_ConstraintWrapper(aNewConstr, theType)); } -ConstraintWrapperPtr createConstraintCollinear( - ConstraintPtr theConstraint, - std::shared_ptr theEntity1, - std::shared_ptr theEntity2) -{ - std::shared_ptr aLine1 = std::dynamic_pointer_cast(theEntity1->entity()); - std::shared_ptr aLine2 = std::dynamic_pointer_cast(theEntity2->entity()); - - // create two point-on-line constraints - std::list aConstrList; - aConstrList.push_back( GCSConstraintPtr(new GCS::ConstraintPointOnLine(aLine2->p1, *aLine1)) ); - aConstrList.push_back( GCSConstraintPtr(new GCS::ConstraintPointOnLine(aLine2->p2, *aLine1)) ); - - return ConstraintWrapperPtr( - new PlaneGCSSolver_ConstraintWrapper(aConstrList, CONSTRAINT_COLLINEAR)); -} - ConstraintWrapperPtr createConstraintParallel( std::shared_ptr theEntity1, std::shared_ptr theEntity2) diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.cpp index ccd7d0675..6e66ae26e 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.cpp +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.cpp @@ -5,9 +5,12 @@ // Author: Artem ZHIDKOV #include +#include +#include #include #include +#include #include void PlaneGCSSolver_UpdateCoincidence::attach(SketchSolver_Constraint* theObserver, @@ -29,7 +32,8 @@ void PlaneGCSSolver_UpdateCoincidence::attach(SketchSolver_Constraint* theObserv void PlaneGCSSolver_UpdateCoincidence::update(const FeaturePtr& theFeature) { if (theFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID() || - theFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) { + theFeature->getKind() == SketchPlugin_ConstraintMiddle::ID() || + theFeature->getKind() == SketchPlugin_ConstraintCollinear::ID()) { myCoincident.clear(); // notify listeners and stop procesing std::list::iterator anIt = myObservers.begin(); @@ -87,6 +91,30 @@ bool PlaneGCSSolver_UpdateCoincidence::checkCoincidence( return isAccepted; } +bool PlaneGCSSolver_UpdateCoincidence::isPointOnEntity( + const EntityWrapperPtr& thePoint, + const EntityWrapperPtr& theEntity) +{ + std::list::iterator anIt = myCoincident.begin(); + for (; anIt != myCoincident.end(); ++anIt) + if (anIt->isExist(thePoint)) + break; + + if (anIt == myCoincident.end()) + return false; + + if (anIt->isExist(theEntity)) + return true; + + if (theEntity->type() == ENTITY_LINE) { + std::shared_ptr aLine = std::dynamic_pointer_cast( + std::dynamic_pointer_cast(theEntity)->entity()); + return anIt->isExist(aLine->p1) || anIt->isExist(aLine->p2); + } + return false; +} + + @@ -127,6 +155,36 @@ bool PlaneGCSSolver_UpdateCoincidence::CoincidentEntities::isExist( return false; } +static bool isEqual(const GCS::Point& thePoint1, const GCS::Point& thePoint2) +{ + return thePoint1.x == thePoint2.x && thePoint1.y == thePoint2.y; +} + +bool PlaneGCSSolver_UpdateCoincidence::CoincidentEntities::isExist( + const GCS::Point& thePoint) const +{ + std::map >::const_iterator + anIt = myExternalAndConnected.begin(); + for (; anIt != myExternalAndConnected.end(); ++anIt) { + if (anIt->first && anIt->first->type() == ENTITY_POINT) { + const GCSPointPtr& aPoint = + std::dynamic_pointer_cast(anIt->first)->point(); + if (isEqual(*aPoint, thePoint)) + return true; + } + + std::set::const_iterator anEntIt = anIt->second.begin(); + for (; anEntIt != anIt->second.end(); ++anEntIt) + if ((*anEntIt)->type() == ENTITY_POINT) { + const GCSPointPtr& aPoint = + std::dynamic_pointer_cast(*anEntIt)->point(); + if (isEqual(*aPoint, thePoint)) + return true; + } + } + return false; +} + bool PlaneGCSSolver_UpdateCoincidence::CoincidentEntities::isNewCoincidence( const EntityWrapperPtr& theEntityExist, const EntityWrapperPtr& theOtherEntity) diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.h index afd49a2a1..9d3b0eb99 100644 --- a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.h +++ b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_UpdateCoincidence.h @@ -10,6 +10,8 @@ #include #include +#include + #include /** \class PlaneGCSSolver_UpdateCoincidence @@ -45,6 +47,10 @@ public: /// \return \c true if the entities does not coincident bool checkCoincidence(const EntityWrapperPtr& theEntity1, const EntityWrapperPtr& theEntity2); + /// \brief Verifies the point is coincident to the feature + /// \return \c true if the point is on the feature + bool isPointOnEntity(const EntityWrapperPtr& thePoint, const EntityWrapperPtr& theEntity); + private: /// \brief Container for collecting and operating coincident entities class CoincidentEntities @@ -55,6 +61,8 @@ private: /// Verify the entity is already in the list bool isExist(const EntityWrapperPtr& theEntity) const; + /// Verify the point is already in the list + bool isExist(const GCS::Point& thePoint) const; /// Check the coincidence is not in list yet bool isNewCoincidence(const EntityWrapperPtr& theEntityExist, const EntityWrapperPtr& theOtherEntity); diff --git a/src/SketchSolver/SketchSolver_ConstraintCoincidence.h b/src/SketchSolver/SketchSolver_ConstraintCoincidence.h index 9169f0021..59061a302 100644 --- a/src/SketchSolver/SketchSolver_ConstraintCoincidence.h +++ b/src/SketchSolver/SketchSolver_ConstraintCoincidence.h @@ -31,7 +31,7 @@ public: virtual bool remove(); protected: - /// \brief Converts SketchPlugin constraint to a list of SolveSpace constraints + /// \brief Converts SketchPlugin constraint to a list of solver constraints virtual void process(); /// \brief Generate list of attributes of constraint in order useful for constraints diff --git a/src/SketchSolver/SketchSolver_ConstraintCollinear.cpp b/src/SketchSolver/SketchSolver_ConstraintCollinear.cpp index f99ab425f..0d35cf485 100644 --- a/src/SketchSolver/SketchSolver_ConstraintCollinear.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintCollinear.cpp @@ -1,56 +1,121 @@ // Copyright (C) 2014-20xx CEA/DEN, EDF R&D #include -#include +#include + +#include +#include +#include +#include #include -SketchSolver_ConstraintCollinear::SketchSolver_ConstraintCollinear(ConstraintPtr theConstraint) - : SketchSolver_Constraint(theConstraint) +static ConstraintWrapperPtr createPointsOnLine( + std::shared_ptr thePoint1, + std::shared_ptr thePoint2, + std::shared_ptr theLine) +{ + std::shared_ptr aGCSLine = std::dynamic_pointer_cast(theLine->entity()); + + std::list aConstrList; + if (thePoint1) + aConstrList.push_back( GCSConstraintPtr( + new GCS::ConstraintPointOnLine(*thePoint1->point(), *aGCSLine)) ); + if (thePoint2) + aConstrList.push_back( GCSConstraintPtr( + new GCS::ConstraintPointOnLine(*thePoint2->point(), *aGCSLine)) ); + + return aConstrList.empty() ? ConstraintWrapperPtr() : ConstraintWrapperPtr( + new PlaneGCSSolver_ConstraintWrapper(aConstrList, CONSTRAINT_COLLINEAR)); +} + + +void SketchSolver_ConstraintCollinear::process() { + cleanErrorMsg(); + if (!myBaseConstraint || !myStorage) { + // Not enough parameters are assigned + return; + } + + EntityWrapperPtr aValue; + std::vector anAttributes; + getAttributes(aValue, anAttributes); + if (!myErrorMsg.empty()) + return; + if (anAttributes.empty()) { + myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE(); + return; + } + + for (int i = 0; i < 2; ++i) { + AttributeRefAttrPtr aRefAttr = myBaseConstraint->refattr(SketchPlugin_Constraint::ATTRIBUTE(i)); + FeaturePtr aLine = ModelAPI_Feature::feature(aRefAttr->object()); + myPoints[2*i] = myStorage->entity(aLine->attribute(SketchPlugin_Line::START_ID())); + myPoints[2*i + 1] = myStorage->entity(aLine->attribute(SketchPlugin_Line::END_ID())); + } + + myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP()); + myStorage->notify(myBaseConstraint); } -////void SketchSolver_ConstraintCollinear::notifyCoincidenceChanged( -//// EntityWrapperPtr theCoincAttr1, -//// EntityWrapperPtr theCoincAttr2) -////{ -//// bool used = true; -//// -//// // obtain IDs of all boundary points of lines -//// EntityID aPointIDs[4]; -//// for (int i = 0; i < 2; ++i) { -//// AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( -//// myBaseConstraint->attribute(SketchPlugin_Constraint::ATTRIBUTE(i))); -//// if (!aRefAttr->object()) -//// continue; -//// FeaturePtr aLine = ModelAPI_Feature::feature(aRefAttr->object()); -//// AttributePtr aLinePt = aLine->attribute(SketchPlugin_Line::START_ID()); -//// aPointIDs[2*i] = myStorage->entity(aLinePt)->id(); -//// aLinePt = aLine->attribute(SketchPlugin_Line::END_ID()); -//// aPointIDs[2*i + 1] = myStorage->entity(aLinePt)->id(); -//// } -//// -//// EntityWrapperPtr anAttrs[2] = {theCoincAttr1, theCoincAttr2}; -//// for (int i = 0; i < 2 && used; ++i) { -//// if (anAttrs[i]->baseAttribute()) -//// used = used && isUsed(anAttrs[i]->baseAttribute()); -//// else -//// used = used && isUsed(anAttrs[i]->baseFeature()); -//// -//// if (!used) { -//// if (anAttrs[i]->type() == ENTITY_POINT) { -//// EntityID anID = anAttrs[i]->id(); -//// for (int j = 0; j < 4; ++j) -//// if (anID == aPointIDs[j]) { -//// used = true; -//// break; -//// } -//// } -//// } -//// } -//// -//// if (used) { -//// remove(); -//// process(); -//// } -////} +void SketchSolver_ConstraintCollinear::notify(const FeaturePtr& theFeature, + PlaneGCSSolver_Update* theUpdater) +{ + PlaneGCSSolver_UpdateCoincidence* anUpdater = + static_cast(theUpdater); + + bool isPointOnOppositeLine[4]; + std::list::reverse_iterator anIt = myAttributes.rbegin(); + for (int i = 0; i < 2; ++i, ++anIt) { + isPointOnOppositeLine[2*i] = anUpdater->isPointOnEntity(myPoints[2*i], *anIt); + isPointOnOppositeLine[2*i + 1] = anUpdater->isPointOnEntity(myPoints[2*i + 1], *anIt); + } + + // both points of one line is on another line => lines are already collinear, + // would like to show "conflicting constraints" + if (isPointOnOppositeLine[0] && isPointOnOppositeLine[1]) + isPointOnOppositeLine[1] = false; + if (isPointOnOppositeLine[2] && isPointOnOppositeLine[3]) + isPointOnOppositeLine[3] = false; + + bool aConstraintToApply[4] = {false, false, false, false}; + ConstraintWrapperPtr aNewConstraint; + std::shared_ptr aPoints[2]; + std::shared_ptr aLine; + + if (isPointOnOppositeLine[0] || isPointOnOppositeLine[1]) { + // one of points of first line is already on the second line, + // make another point of first line to be coincident with second line + for (int i = 0; i < 2; ++i) { + if (isPointOnOppositeLine[i]) + continue; + + if (!myIsConstraintApplied[i]) + aPoints[i] = std::dynamic_pointer_cast(myPoints[i]); + aConstraintToApply[i] = true; + } + aLine = std::dynamic_pointer_cast(myAttributes.back()); + } else { + // verify second line and add necessary constraints + for (int i = 0; i < 2; ++i) { + if (isPointOnOppositeLine[i + 2]) + continue; + + if (!myIsConstraintApplied[i+2]) + aPoints[i] = std::dynamic_pointer_cast(myPoints[i + 2]); + aConstraintToApply[i+2] = true; + } + aLine = std::dynamic_pointer_cast(myAttributes.front()); + } + + bool isNew = false; + for (int i = 0; i < 4 && !isNew; ++i) + if (aConstraintToApply[i] != myIsConstraintApplied[i]) + isNew = true; + if (isNew) { + mySolverConstraint = createPointsOnLine(aPoints[0], aPoints[1], aLine); + myStorage->removeConstraint(myBaseConstraint); + myStorage->addConstraint(myBaseConstraint, mySolverConstraint); + } +} diff --git a/src/SketchSolver/SketchSolver_ConstraintCollinear.h b/src/SketchSolver/SketchSolver_ConstraintCollinear.h index 7ab635581..a70cc80ac 100644 --- a/src/SketchSolver/SketchSolver_ConstraintCollinear.h +++ b/src/SketchSolver/SketchSolver_ConstraintCollinear.h @@ -17,13 +17,24 @@ class SketchSolver_ConstraintCollinear : public SketchSolver_Constraint { public: /// Constructor based on SketchPlugin constraint - SketchSolver_ConstraintCollinear(ConstraintPtr theConstraint); + SketchSolver_ConstraintCollinear(ConstraintPtr theConstraint) + : SketchSolver_Constraint(theConstraint) + { + for (int i = 0; i < 4; ++i) + myIsConstraintApplied[i] = false; + } - virtual ~SketchSolver_ConstraintCollinear() {} + /// \brief Notify this object about the feature is changed somewhere + virtual void notify(const FeaturePtr& theFeature, + PlaneGCSSolver_Update* theUpdater); -//// /// \brief Notify constraint, that coincidence appears or removed -//// virtual void notifyCoincidenceChanged(EntityWrapperPtr theCoincAttr1, -//// EntityWrapperPtr theCoincAttr2); +protected: + /// \brief Converts SketchPlugin constraint to a list of solver constraints + virtual void process(); + +private: + EntityWrapperPtr myPoints[4]; ///< extremities on collinear lines + bool myIsConstraintApplied[4]; ///< set \c true if point on opposite line }; #endif -- 2.39.2