X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchSolver%2FSketchSolver_ConstraintTangent.cpp;h=e104db4392383c1b52d313f6ed951bb3e72f468d;hb=d5b5ce2284869d8b97ce638502c58c810bbeb0c7;hp=865065411596d9e96ec503398675a63f43cf78a3;hpb=3c445d389d3cbe4c98403bc62e4b6ba8d06cc314;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp index 865065411..e104db439 100644 --- a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp @@ -1,84 +1,388 @@ +// Copyright (C) 2014-2017 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 -#include #include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast(x) + +/// \brief Obtain tangent features from the constraint +static void getTangentFeatures(const ConstraintPtr& theConstraint, + FeaturePtr& theFeature1, + FeaturePtr& theFeature2); + +/// \brief Obtain all coincident constraints between features +static std::set collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2); + +/// \brief Check whether the entities has only one shared point or less. +/// Return list of coincident points. +static std::set coincidentBoundaryPoints(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::process() { cleanErrorMsg(); - if (!myBaseConstraint || !myStorage || myGroup == 0) { - /// TODO: Put error message here + if (!myBaseConstraint || !myStorage) { + // Not enough parameters are assigned return; } - if (!mySlvsConstraints.empty()) // some data is changed, update constraint - update(myBaseConstraint); - double aValue; - std::vector anEntID; - getAttributes(aValue, anEntID); + EntityWrapperPtr aValue; + std::vector 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 aNbArcs = 0; - Slvs_Entity anEntities[2]; - myType = SLVS_C_CURVE_CURVE_TANGENT; - std::vector::iterator anEntIter = anEntID.begin(); - for (; anEntIter != anEntID.end(); anEntIter++) { - Slvs_Entity anEnt = myStorage->getEntity(*anEntIter); - if (anEnt.type == SLVS_E_LINE_SEGMENT) { - if (aNbLines == 0) - anEntities[1 + aNbLines] = anEnt; - aNbLines++; - myType = SLVS_C_ARC_LINE_TANGENT; - } - else if (anEnt.type == SLVS_E_ARC_OF_CIRCLE) { - if (aNbArcs < 2) - anEntities[aNbArcs] = anEnt; - aNbArcs++; - } + int aNbCircles = 0; + std::list::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; } - if (aNbLines + aNbArcs != 2) { + if (aNbCircles < 1) { + myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE(); + return; + } + if (aNbLines == 1 && aNbCircles == 1) { + myType = CONSTRAINT_TANGENT_CIRCLE_LINE; + } + else if (aNbCircles == 2) { + myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE; + isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back()); + } + else { myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE(); return; - } else if (aNbArcs < 1) { - myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE(); + } + + FeaturePtr aFeature1, aFeature2; + getTangentFeatures(myBaseConstraint, aFeature1, aFeature2); + + // check number of coincident points + std::set aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2); + if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) { + myErrorMsg = SketchSolver_Error::TANGENCY_FAILED(); return; } - // It is necessary to identify which points of entities are coincident - int aSlvsOtherFlag = 0; - int aSlvsOther2Flag = 0; - // Obtain start and end points of entities - Slvs_hEntity aPointsToFind[4]; - for (int i = 0; i < 2; i++) { - int aShift = anEntities[i].type == SLVS_E_ARC_OF_CIRCLE ? 1 : 0; - aPointsToFind[2*i] = anEntities[i].point[aShift]; - aPointsToFind[2*i+1]= anEntities[i].point[aShift+1]; - } - // Search coincident points - bool isPointFound = false; - for (int i = 0; i < 2 && !isPointFound; i++) - for (int j = 2; j < 4 && !isPointFound; j++) - if (myStorage->isEqual(aPointsToFind[i], aPointsToFind[j])) { - aSlvsOtherFlag = i; - aSlvsOther2Flag = j - 2; - isPointFound = true; - } - if (!isPointFound) { - // There is no coincident points between tangential objects. Generate error message - myErrorMsg = SketchSolver_Error::NO_COINCIDENT_POINTS(); + EntityWrapperPtr aSharedPointEntity; + if (!aCoincidentPoints.empty()) { + mySharedPoint = *aCoincidentPoints.begin(); + 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() +{ + if (myType == CONSTRAINT_TANGENT_CIRCLE_CIRCLE) { + EntityWrapperPtr anEntity1 = + myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); + EntityWrapperPtr anEntity2 = + myStorage->entity(myBaseConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); + + 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; + } + } + + if (mySharedPoint && !isRebuild) { + // The features are tangent in the shared point, but the coincidence has been removed/updated. + // Check if the coincidence is the same. + std::set aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2); + isRebuild = true; + std::set::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 collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + const std::set& aRefs1 = theFeature1->data()->refsToMe(); + const std::set& aRefs2 = theFeature2->data()->refsToMe(); + + std::set aCoincidences; + std::set::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); } - Slvs_Constraint aConstraint = Slvs_MakeConstraint(SLVS_C_UNKNOWN, myGroup->getId(), - getType(), myGroup->getWorkplaneId(), aValue, - SLVS_E_UNKNOWN, SLVS_E_UNKNOWN, anEntities[0].h, anEntities[1].h); - aConstraint.other = aSlvsOtherFlag; - aConstraint.other2 = aSlvsOther2Flag; - aConstraint.h = myStorage->addConstraint(aConstraint); - mySlvsConstraints.push_back(aConstraint.h); - adjustConstraint(); + // leave only coincidences referred to the second feature + std::set 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 coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + std::set aCoincidences = collectCoincidences(theFeature1, theFeature2); + // collect points only + std::set aCoincidentPoints; + std::set::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; +} + +bool isArcArcTangencyInternal(EntityWrapperPtr theArc1, EntityWrapperPtr theArc2) +{ + std::shared_ptr aCirc1 = std::dynamic_pointer_cast( + GCS_EDGE_WRAPPER(theArc1)->entity()); + std::shared_ptr aCirc2 = std::dynamic_pointer_cast( + 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 aCirc = + std::dynamic_pointer_cast(anEntCirc->entity()); + std::shared_ptr anArc = std::dynamic_pointer_cast(aCirc); + + std::shared_ptr aLine = + std::dynamic_pointer_cast(anEntLine->entity()); + + GCSConstraintPtr aNewConstr; + if (theSharedPoint && anArc) { // do not process shared point between circle and line + GCSPointPtr aPoint = + std::dynamic_pointer_cast(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 aCirc1 = + std::dynamic_pointer_cast(GCS_EDGE_WRAPPER(theEntity1)->entity()); + std::shared_ptr aCirc2 = + std::dynamic_pointer_cast(GCS_EDGE_WRAPPER(theEntity2)->entity()); + + GCSConstraintPtr aNewConstr; + if (theSharedPoint) { + GCSPointPtr aPoint = + std::dynamic_pointer_cast(theSharedPoint)->point(); + + adjustAngleBetweenCurves(aCirc1, aCirc2, aPoint, theAngle); + aNewConstr = + GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCirc1, *aCirc2, *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)); +}