from TestSketcher import SketcherTestCase
class SketcherSetFillet(SketcherTestCase):
- # TODO: Remove tearDown method to check Python dump in super-class
- def tearDown(self):
- model.end()
- #assert(model.checkPythonDump())
- model.reset()
-
def test_fillet(self):
l1 = self.sketch.addLine(0, 0, 0, 1)
l2 = self.sketch.addLine(0, 1, 1, 1)
assert (math.fabs(aLine2Len - anExtLineLen) < 1.e-10)
assert (model.dof(aSketchFeature) == 12)
#=========================================================================
+# Remove costraint to check the DOF
+#=========================================================================
+aSession.startOperation()
+aDocument.removeFeature(aConstraintEqLen2)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 13)
+#=========================================================================
# End of test
#=========================================================================
# Verify the objects of fillet are created
#=========================================================================
checkSmoothness(aSketchFeature)
-assert model.dof(aSketchFeature) == 14, "PlaneGCS limitation: if you see this message, then maybe PlaneGCS has solved DoF for sketch with fillet correctly (expected DoF = 10, observed = {0}".format(model.dof(aSketchFeature))
+assert (model.dof(aSketchFeature) == 10)
+#=========================================================================
+# Move a line and check the fillet is correct
+#=========================================================================
+DELTA_X = DELTA_Y = 10.
+aSession.startOperation()
+aEndPoint1.setValue(aEndPoint1.x() + DELTA_X, aEndPoint1.y() + DELTA_Y)
+aSession.finishOperation()
+checkSmoothness(aSketchFeature)
+assert (model.dof(aSketchFeature) == 10)
+
#=========================================================================
# Create another sketch
# Verify the objects of fillet are created
#=========================================================================
checkSmoothness(aSketchFeature)
-assert model.dof(aSketchFeature) == 10, "PlaneGCS limitation: if you see this message, then maybe PlaneGCS has solved DoF for sketch with fillet correctly (expected DoF = 8, observed = {0}".format(model.dof(aSketchFeature))
+assert (model.dof(aSketchFeature) == 8)
+#=========================================================================
+# Move a line and check the fillet is correct
+#=========================================================================
+DELTA_X = 1.
+DELTA_Y = -2.
+aSession.startOperation()
+aStartPoint1.setValue(aStartPoint1.x() + DELTA_X, aStartPoint1.y() + DELTA_Y)
+aSession.finishOperation()
+checkSmoothness(aSketchFeature)
+assert (model.dof(aSketchFeature) == 8)
#=========================================================================
# End of test
#=========================================================================
# TODO: Improve Fillet test case by moving one of filleted objectes and check coincidence and tangency are correct
-# TODO: Checking of Python dump has been disabled until the Fillet redesigned.
-#assert(model.checkPythonDump())
+assert(model.checkPythonDump())
def tearDown(self):
model.end()
- # TODO: Revert commented line to check Python dump
- #self.assertTrue(model.checkPythonDump())
+ self.assertTrue(model.checkPythonDump())
model.reset()
return [aFeatureA, aFeatureB]
- @unittest.expectedFailure
def test_fillet_two_lines(self):
""" Test 1. Fillet on two connected lines
"""
# remove fillet for correct python dump
self.myDocument.removeFeature(aFillet.feature())
- @unittest.expectedFailure
def test_fillet_arc_line(self):
""" Test 3. Fillet on connected arc and line
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_two_arcs(self):
""" Test 4. Fillet on two connected arcs
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_horizontal_vertical(self):
""" Test 5. Fillet on two connected lines in case of Horizontal or Vertical constraints applied
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_orthogonal(self):
""" Test 6. Fillet on two connected lines in case of Perpendicular constraint applied
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_parallel(self):
""" Test 7. Fillet on two connected lines in case of Parallel constraint applied
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [4])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [8])
- @unittest.expectedFailure
def test_fillet_with_equal_lines(self):
""" Test 8. Fillet on two connected lines in case of Equal constraint applied
"""
model.do()
self.checkDOF()
self.mySketch.setFillet(aSketchLineA.startPoint())
- self.myDOF += 2
+ self.myDOF += 2 # Equal has been removed
model.do()
self.checkFillet()
self.checkDOF()
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_equal_arcs(self):
""" Test 9. Fillet on two connected arcs in case of Equal constraint applied
"""
model.do()
self.checkDOF()
self.mySketch.setFillet(aSketchArc1.endPoint())
- self.myDOF += 1
+ self.myDOF += 2 # Equal has been removed
model.do()
self.checkFillet()
self.checkDOF()
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_length(self):
""" Test 10. Fillet on two connected lines in case of Length constraint applied
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_radius(self):
""" Test 11. Fillet on connected arc and line in case of Radius constraint is applied to arc
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_distance(self):
""" Test 12. Fillet on two connected lines in case of Distance constraint applied
"""
model.do()
self.checkDOF()
self.mySketch.setFillet(aSketchLineA.startPoint())
- self.myDOF += 1
+ self.myDOF += 2 # Distance has been removed
model.do()
self.checkFillet()
self.checkDOF()
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [4])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [8])
- @unittest.expectedFailure
def test_fillet_with_fixed_point(self):
""" Test 13. Fillet on two connected lines in case of Fixed constraint applied to the fillet point
"""
model.do()
self.checkDOF()
self.mySketch.setFillet(aSketchLineA.startPoint())
- self.myDOF += 1
+ self.myDOF += 3 # Fixed constraint has been removed
model.do()
self.checkFillet()
self.checkDOF()
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_fixed_line(self):
""" Test 14. Fillet on two connected lines in case of Fixed constraint applied to one of lines
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.EDGE, [3])
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
- @unittest.expectedFailure
def test_fillet_with_angle(self):
""" Test 15. Fillet on two connected lines in case of Perpendicular constraint applied
"""
model.testNbSubShapes(self.mySketch, GeomAPI_Shape.VERTEX, [6])
-# TODO: Remove unittest.expectedFailure if the Tangency in PlaneGCS will be updated
if __name__ == '__main__':
unittest.main()
GCSCurvePtr myEntity;
};
+typedef std::shared_ptr<PlaneGCSSolver_EdgeWrapper> EdgeWrapperPtr;
+
#endif
myDOF = 0;
}
-void PlaneGCSSolver_Solver::addConstraint(GCSConstraintPtr theConstraint,
- const SketchSolver_ConstraintType theType)
+void PlaneGCSSolver_Solver::addConstraint(GCSConstraintPtr theConstraint)
{
myEquationSystem->addConstraint(theConstraint.get());
myConstraints[theConstraint->getTag()].insert(theConstraint);
myDOF = -1;
-
- // Workaround: avoid tangent constraint in the list of redundant
- if (theType == CONSTRAINT_TANGENT_CIRCLE_LINE ||
- theType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE ||
- theType == CONSTRAINT_PT_PT_COINCIDENT ||
- theType == CONSTRAINT_PT_ON_CIRCLE ||
- theType == CONSTRAINT_PT_ON_LINE)
- myConstraintIDsNotRedundant.insert(theConstraint->getTag());
}
void PlaneGCSSolver_Solver::removeConstraint(ConstraintID theID)
{
myConstraints.erase(theID);
- myConstraintIDsNotRedundant.erase(theID);
if (myConstraints.empty()) {
myEquationSystem->clear();
myDOF = (int)myParameters.size();
myConflictingIDs.insert(aConflict.begin(), aConflict.end());
myEquationSystem->getRedundant(aConflict);
- // Workaround: avoid conflicting tangent constraints
- GCS::VEC_I aTemp = aConflict;
- aConflict.clear();
- for (GCS::VEC_I::iterator anIt = aTemp.begin(); anIt != aTemp.end(); ++anIt)
- if (myConstraintIDsNotRedundant.find(*anIt) == myConstraintIDsNotRedundant.end())
- aConflict.push_back(*anIt);
myConflictingIDs.insert(aConflict.begin(), aConflict.end());
myConfCollected = true;
void clear();
/// \brief Add constraint to the system of equations
- void addConstraint(GCSConstraintPtr theConstraint, const SketchSolver_ConstraintType theType);
+ void addConstraint(GCSConstraintPtr theConstraint);
/// \brief Remove constraint from the system of equations
void removeConstraint(ConstraintID theID);
GCS::VEC_pD myParameters; ///< list of unknowns
ConstraintMap myConstraints; ///< list of constraints
- /// IDs of constraints (coincidence, tangency) which will not be treated as conflicting
- /// if they are reported as redundant
- GCS::SET_I myConstraintIDsNotRedundant;
-
std::shared_ptr<GCS::System> myEquationSystem; ///< set of equations for solving in FreeGCS
GCS::SET_I myConflictingIDs; ///< list of IDs of conflicting constraints
std::dynamic_pointer_cast<PlaneGCSSolver_ConstraintWrapper>(theConstraint)->constraints();
std::list<GCSConstraintPtr>::const_iterator anIt = aConstraints.begin();
for (; anIt != aConstraints.end(); ++anIt)
- theSolver->addConstraint(*anIt, theConstraint->type());
+ theSolver->addConstraint(*anIt);
}
std::map<ConstraintPtr, ConstraintWrapperPtr>::iterator
aFound = myConstraintMap.find(theConstraint);
if (aFound != myConstraintMap.end()) {
- ConstraintID anID = aFound->second->id();
+ ConstraintWrapperPtr aCW = aFound->second;
+ ConstraintID anID = aCW->id();
+
// Remove solver's constraints
mySketchSolver->removeConstraint(anID);
+
+ // Remove value if exists
+ const ScalarWrapperPtr& aValue = aCW->valueParameter();
+ if (aValue) {
+ GCS::SET_pD aParToRemove;
+ aParToRemove.insert(aValue->scalar());
+ removeParameters(aParToRemove);
+ }
+
// Remove constraint
myConstraintMap.erase(aFound);
for (; aFIter != myFeatureMap.end(); aFIter++)
if (!aFIter->first->data() || !aFIter->first->data()->isValid()) {
anInvalidFeatures.push_back(aFIter->first);
- aDestroyer.remove(aFIter->second);
+ if (aFIter->second)
+ aDestroyer.remove(aFIter->second);
// remove invalid arc
std::map<EntityWrapperPtr, ConstraintWrapperPtr>::iterator
std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2,
std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theIntermed);
-static ConstraintWrapperPtr
- createConstraintTangent(const SketchSolver_ConstraintType& theType,
- std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
- std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2);
static ConstraintWrapperPtr
createConstraintMiddlePoint(std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity);
GCS_EDGE_WRAPPER(theEntity2),
anIntermediate);
break;
- case CONSTRAINT_TANGENT_CIRCLE_LINE:
- case CONSTRAINT_TANGENT_CIRCLE_CIRCLE:
- aResult = createConstraintTangent(theType,
- GCS_EDGE_WRAPPER(theEntity1),
- GCS_EDGE_WRAPPER(theEntity2));
- break;
- case CONSTRAINT_MULTI_TRANSLATION:
- case CONSTRAINT_MULTI_ROTATION:
- case CONSTRAINT_SYMMETRIC:
default:
break;
}
aResult->setValueParameter(theIntermed);
return aResult;
}
-
-ConstraintWrapperPtr createConstraintTangent(
- const SketchSolver_ConstraintType& theType,
- std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity1,
- std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2)
-{
- GCSConstraintPtr aNewConstr;
- if (theType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
- GCSCurvePtr anEntCirc, anEntLine;
- if (theEntity1->type() == ENTITY_LINE) {
- anEntLine = theEntity1->entity();
- anEntCirc = theEntity2->entity();
- } else {
- anEntLine = theEntity2->entity();
- anEntCirc = theEntity1->entity();
- }
-
- std::shared_ptr<GCS::Circle> aCirc =
- std::dynamic_pointer_cast<GCS::Circle>(anEntCirc);
- std::shared_ptr<GCS::Line> aLine =
- std::dynamic_pointer_cast<GCS::Line>(anEntLine);
-
- aNewConstr =
- GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
- } else {
- std::shared_ptr<GCS::Circle> aCirc1 =
- std::dynamic_pointer_cast<GCS::Circle>(theEntity1->entity());
- std::shared_ptr<GCS::Circle> aCirc2 =
- std::dynamic_pointer_cast<GCS::Circle>(theEntity2->entity());
-
- double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
- double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
- double aDist = sqrt(aDX * aDX + aDY * aDY);
- aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
- aCirc1->rad, aCirc2->rad, (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad))));
- }
-
- return ConstraintWrapperPtr(new PlaneGCSSolver_ConstraintWrapper(aNewConstr, theType));
-}
-
-bool PlaneGCSSolver_Tools::isArcArcTangencyInternal(
- EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
-{
- std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
- GCS_EDGE_WRAPPER(theArc1)->entity());
- std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
- GCS_EDGE_WRAPPER(theArc2)->entity());
-
- if (!aCirc1 || !aCirc2)
- return false;
-
- double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
- double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
- double aDist = sqrt(aDX * aDX + aDY * aDY);
-
- return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
-}
/// \brief Convert entity to line
/// \return empty pointer if the entity is not a line
std::shared_ptr<GeomAPI_Lin2d> line(FeaturePtr theFeature);
-
- /// \brief Check if two connected arcs have centers
- /// in same direction relatively to connection point
- bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
- EntityWrapperPtr theArc2);
};
#endif
#include <SketchSolver_ConstraintTangent.h>
#include <SketchSolver_Error.h>
+#include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointWrapper.h>
#include <PlaneGCSSolver_Tools.h>
+#include <PlaneGCSSolver_UpdateCoincidence.h>
#include <GeomAPI_Pnt2d.h>
#include <SketchPlugin_Circle.h>
#include <cmath>
+#define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(x)
-/// \brief Check whether the entities has only one shared point or less
-static bool hasSingleCoincidence(FeaturePtr theFeature1, FeaturePtr theFeature2)
-{
- const std::set<AttributePtr>& aRefs1 = theFeature1->data()->refsToMe();
- const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
+/// \brief Obtain tangent features from the constraint
+static void getTangentFeatures(const ConstraintPtr& theConstraint,
+ FeaturePtr& theFeature1,
+ FeaturePtr& theFeature2);
- // collect all shared coincidendes
- std::set<FeaturePtr> aCoincidences;
- std::set<AttributePtr>::const_iterator anIt;
- 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);
- }
- int aNbCoinCidentPoints = 0;
- for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) {
- FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
- if (aCoincidences.find(aRef) != aCoincidences.end())
- ++aNbCoinCidentPoints;
- }
+/// \brief Obtain all coincident constraints between features
+static std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2);
- return aNbCoinCidentPoints <= 1;
-}
+/// \brief Check whether the entities has only one shared point or less.
+/// Return list of coincident points.
+static std::list<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2);
+
+/// \brief Check if two connected arcs have centers
+/// in same direction relatively to connection point
+static bool isArcArcTangencyInternal(EntityWrapperPtr theArc1,
+ EntityWrapperPtr theArc2);
+
+static ConstraintWrapperPtr
+ createArcLineTangency(EntityWrapperPtr theEntity1,
+ EntityWrapperPtr theEntity2,
+ EntityWrapperPtr theShapedPoint = EntityWrapperPtr(),
+ double* theAngle = 0);
+
+static ConstraintWrapperPtr
+ createArcArcTangency(EntityWrapperPtr theEntity1,
+ EntityWrapperPtr theEntity2,
+ bool theInternalTangency,
+ EntityWrapperPtr theSharedPoint = EntityWrapperPtr(),
+ double* theAngle = 0);
-void SketchSolver_ConstraintTangent::getAttributes(
- EntityWrapperPtr& theValue,
- std::vector<EntityWrapperPtr>& theAttributes)
+
+void SketchSolver_ConstraintTangent::process()
{
- SketchSolver_Constraint::getAttributes(theValue, theAttributes);
- if (!myErrorMsg.empty() || !theAttributes[2] || !theAttributes[3]) {
- theAttributes.clear();
+ 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_ConstraintTangent::rebuild()
+{
+ if (mySolverConstraint)
+ myStorage->removeConstraint(myBaseConstraint);
+
+ mySolverConstraint = ConstraintWrapperPtr();
+ mySharedPoint = AttributePtr();
+
// Check the quantity of entities of each type and their order (arcs first)
int aNbLines = 0;
int aNbCircles = 0;
- std::vector<EntityWrapperPtr>::iterator anEntIt = theAttributes.begin() + 2;
- for (; anEntIt != theAttributes.end(); ++anEntIt) {
+ 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)
}
else if (aNbCircles == 2) {
myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE;
- isArcArcInternal =
- PlaneGCSSolver_Tools::isArcArcTangencyInternal(theAttributes[2], theAttributes[3]);
+ isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
}
else {
myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
return;
}
- if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
- AttributeRefAttrPtr aRefAttr = myBaseConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
- FeaturePtr aFeature1 = ModelAPI_Feature::feature(aRefAttr->object());
- aRefAttr = myBaseConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
- FeaturePtr aFeature2 = ModelAPI_Feature::feature(aRefAttr->object());
+ FeaturePtr aFeature1, aFeature2;
+ getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
- if (!hasSingleCoincidence(aFeature1, aFeature2))
- myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
+ // check number of coincident points
+ std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
+ if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 1) {
+ myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
+ return;
}
+
+ EntityWrapperPtr aSharedPointEntity;
+ if (!aCoincidentPoints.empty()) {
+ mySharedPoint = aCoincidentPoints.front();
+ aSharedPointEntity = myStorage->entity(mySharedPoint);
+ }
+
+ if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
+ mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
+ aSharedPointEntity, &myCurveCurveAngle);
+ } else {
+ mySolverConstraint = createArcArcTangency(myAttributes.front(), myAttributes.back(),
+ isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
+ }
+
+ myStorage->addConstraint(myBaseConstraint, mySolverConstraint);
}
void SketchSolver_ConstraintTangent::adjustConstraint()
EntityWrapperPtr anEntity2 =
myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
- if (isArcArcInternal != PlaneGCSSolver_Tools::isArcArcTangencyInternal(anEntity1, anEntity2)) {
- // fully rebuld constraint, because it is unable to access attributes of PlaneGCS constraint
- remove();
- process();
+ if (isArcArcInternal != isArcArcTangencyInternal(anEntity1, anEntity2))
+ rebuild();
+ }
+}
+
+void SketchSolver_ConstraintTangent::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;
+ getTangentFeatures(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;
+ }
+ } else if (mySharedPoint) {
+ // The features are tangent in the shared point, but the coincidence has been removed.
+ // Check if the coincidence is the same.
+ std::list<AttributePtr> aCoincidentPoints = coincidentPoints(aTgFeat1, aTgFeat2);
+ isRebuild = true;
+ std::list<AttributePtr>::iterator anIt = aCoincidentPoints.begin();
+ for (; anIt != aCoincidentPoints.end() && isRebuild; ++anIt)
+ if (*anIt == mySharedPoint)
+ isRebuild = false; // the coincidence is still exists => nothing to change
+ }
+
+ if (isRebuild)
+ rebuild();
+}
+
+
+
+
+// ================== Auxiliary functions =================================
+void getTangentFeatures(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());
+}
+
+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::list<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+ std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
+ // collect points only
+ std::list<AttributePtr> aCoincidentPoints;
+ std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
+ for (; aCIt != aCoincidences.end(); ++ aCIt) {
+ for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+ AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
+ if (aRefAttr && !aRefAttr->isObject()) {
+ aCoincidentPoints.push_back(aRefAttr->attr());
+ break;
+ }
}
}
+ return aCoincidentPoints;
+}
+
+bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2)
+{
+ std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(
+ GCS_EDGE_WRAPPER(theArc1)->entity());
+ std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(
+ GCS_EDGE_WRAPPER(theArc2)->entity());
+
+ if (!aCirc1 || !aCirc2)
+ return false;
+
+ double aDX = *(aCirc1->center.x) - *(aCirc2->center.x);
+ double aDY = *(aCirc1->center.y) - *(aCirc2->center.y);
+ double aDist = sqrt(aDX * aDX + aDY * aDY);
+
+ return (aDist < *(aCirc1->rad) || aDist < *(aCirc2->rad));
+}
+
+// sets angle to 0 or 180 degrees
+static 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 (fabs(anAngle) <= PI / 2.)
+ *theAngle = 0.0;
+ else
+ *theAngle = PI;
+}
+
+
+ConstraintWrapperPtr createArcLineTangency(EntityWrapperPtr theEntity1,
+ EntityWrapperPtr theEntity2,
+ EntityWrapperPtr theSharedPoint,
+ double* theAngle)
+{
+ EdgeWrapperPtr anEntLine, anEntCirc;
+ if (theEntity1->type() == ENTITY_LINE) {
+ anEntLine = GCS_EDGE_WRAPPER(theEntity1);
+ anEntCirc = GCS_EDGE_WRAPPER(theEntity2);
+ } else {
+ anEntLine = GCS_EDGE_WRAPPER(theEntity2);
+ anEntCirc = GCS_EDGE_WRAPPER(theEntity1);
+ }
+
+ std::shared_ptr<GCS::Circle> aCirc =
+ std::dynamic_pointer_cast<GCS::Circle>(anEntCirc->entity());
+ std::shared_ptr<GCS::Line> aLine =
+ std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
+
+ GCSConstraintPtr aNewConstr;
+ if (theSharedPoint) {
+ std::shared_ptr<GCS::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
+ GCSPointPtr aPoint =
+ std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
+
+ adjustAngleBetweenCurves(anArc, aLine, aPoint, theAngle);
+ aNewConstr =
+ GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc, *aLine, *aPoint, theAngle));
+ } else {
+ aNewConstr =
+ GCSConstraintPtr(new GCS::ConstraintP2LDistance(aCirc->center, *aLine, aCirc->rad));
+ }
+
+ return ConstraintWrapperPtr(
+ new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_LINE));
+}
+
+ConstraintWrapperPtr createArcArcTangency(EntityWrapperPtr theEntity1,
+ EntityWrapperPtr theEntity2,
+ bool theInternalTangency,
+ EntityWrapperPtr theSharedPoint,
+ double* theAngle)
+{
+ std::shared_ptr<GCS::Circle> aCirc1 =
+ std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity1)->entity());
+ std::shared_ptr<GCS::Circle> aCirc2 =
+ std::dynamic_pointer_cast<GCS::Circle>(GCS_EDGE_WRAPPER(theEntity2)->entity());
+
+ GCSConstraintPtr aNewConstr;
+ if (theSharedPoint) {
+ std::shared_ptr<GCS::Arc> anArc1 = std::dynamic_pointer_cast<GCS::Arc>(aCirc1);
+ std::shared_ptr<GCS::Arc> anArc2 = std::dynamic_pointer_cast<GCS::Arc>(aCirc2);
+ GCSPointPtr aPoint =
+ std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
+
+ adjustAngleBetweenCurves(anArc1, anArc2, aPoint, theAngle);
+ aNewConstr =
+ GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*anArc1, *anArc2, *aPoint, theAngle));
+ } else {
+ aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
+ aCirc1->rad, aCirc2->rad, theInternalTangency));
+ }
+
+ return ConstraintWrapperPtr(
+ new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CIRCLE_CIRCLE));
}
/// Constructor based on SketchPlugin constraint
SketchSolver_ConstraintTangent(ConstraintPtr theConstraint) :
SketchSolver_Constraint(theConstraint),
- isArcArcInternal(false)
+ isArcArcInternal(false),
+ myCurveCurveAngle(0.0)
{}
+ /// \brief Notify this object about the feature is changed somewhere
+ virtual void notify(const FeaturePtr& theFeature,
+ PlaneGCSSolver_Update* theUpdater);
+
protected:
- /// \brief Generate list of attributes of constraint in order useful for constraints
- /// \param[out] theValue numerical characteristic of constraint (e.g. distance)
- /// \param[out] theAttributes list of attributes to be filled
- virtual void getAttributes(EntityWrapperPtr& theValue,
- std::vector<EntityWrapperPtr>& theAttributes);
+ /// \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();
/// \brief This method is used in derived objects to check consistency of constraint.
/// E.g. the distance between line and point may be signed.
private:
bool isArcArcInternal;
+ double myCurveCurveAngle;
+ AttributePtr mySharedPoint;
};
#endif