X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;ds=inline;f=src%2FSketchSolver%2FSketchSolver_ConstraintTangent.cpp;h=0bc96168e6880ae6036d54b4b863af10501dc555;hb=5af7dc6c1ea31200ade67ccac78e98b4b1cec8a2;hp=ab4a27ea793b8e15da0c0dbd54cbaefa7086f07c;hpb=2982303b80faa9bed2a7715d2778fd10d8cd0465;p=modules%2Fshaper.git diff --git a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp index ab4a27ea7..0bc96168e 100644 --- a/src/SketchSolver/SketchSolver_ConstraintTangent.cpp +++ b/src/SketchSolver/SketchSolver_ConstraintTangent.cpp @@ -3,116 +3,365 @@ #include #include +#include +#include #include +#include #include +#include #include #include #include +#define GCS_EDGE_WRAPPER(x) std::dynamic_pointer_cast(x) -/// \brief Check whether the entities has only one shared point or less -static bool hasSingleCoincidence(FeaturePtr theFeature1, FeaturePtr theFeature2) -{ - const std::set& aRefs1 = theFeature1->data()->refsToMe(); - const std::set& 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 aCoincidences; - std::set::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); - } - for (anIt = aRefs2.begin(); anIt != aRefs2.end(); ++anIt) { - FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner()); - if (aRef) - aCoincidences.erase(aRef); - } +/// \brief Obtain all coincident constraints between features +static std::set collectCoincidences(FeaturePtr theFeature1, FeaturePtr theFeature2); - return aCoincidences.size() <= 1; -} +/// \brief Check whether the entities has only one shared point or less. +/// Return list of coincident points. +static std::list 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::getAttributes( - EntityWrapperPtr& theValue, - std::vector& 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 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; int aNbCircles = 0; - bool isSwap = false; // whether need to swap arguments (arc goes before line) - std::vector::iterator anEntIt = theAttributes.begin() + 2; - for (; anEntIt != theAttributes.end(); ++anEntIt) { + 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) { - ++aNbArcs; - isSwap = aNbLines > 0; - } - else if ((*anEntIt)->type() == ENTITY_CIRCLE) { + else if ((*anEntIt)->type() == ENTITY_ARC || (*anEntIt)->type() == ENTITY_CIRCLE) ++aNbCircles; - isSwap = aNbLines > 0; - } } - if (aNbArcs < 1 && aNbCircles < 1) { + if (aNbCircles < 1) { myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE(); return; } - if (aNbLines == 1) { - if (aNbArcs == 1) - myType = CONSTRAINT_TANGENT_ARC_LINE; - else if (aNbCircles == 1) - myType = CONSTRAINT_TANGENT_CIRCLE_LINE; + if (aNbLines == 1 && aNbCircles == 1) { + myType = CONSTRAINT_TANGENT_CIRCLE_LINE; } - else if (aNbArcs == 2) { - myType = CONSTRAINT_TANGENT_ARC_ARC; - isArcArcInternal = - PlaneGCSSolver_Tools::isArcArcTangencyInternal(theAttributes[2], theAttributes[3]); + else if (aNbCircles == 2) { + myType = CONSTRAINT_TANGENT_CIRCLE_CIRCLE; + isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back()); } else { myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE(); return; } - if (myType == CONSTRAINT_TANGENT_ARC_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 aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2); + if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 1) { + myErrorMsg = SketchSolver_Error::TANGENCY_FAILED(); + return; } - if (isSwap) { - EntityWrapperPtr aTemp = theAttributes[2]; - theAttributes[2] = theAttributes[3]; - theAttributes[3] = aTemp; + 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() { - if (myType == CONSTRAINT_TANGENT_ARC_ARC) { + 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 != 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 aCoincidentPoints = coincidentBoundaryPoints(aTgFeat1, aTgFeat2); + isRebuild = true; + std::list::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); + } + + // 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::list coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2) +{ + std::set aCoincidences = collectCoincidences(theFeature1, theFeature2); + // collect points only + std::list aCoincidentPoints; + std::set::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()) + continue; + + AttributePtr anAttr = aRefAttr->attr(); + if (anAttr->id() != SketchPlugin_Arc::CENTER_ID() && + anAttr->id() != SketchPlugin_Circle::CENTER_ID()) { + aCoincidentPoints.push_back(aRefAttr->attr()); + break; + } } } + 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) { + std::shared_ptr anArc1 = std::dynamic_pointer_cast(aCirc1); + std::shared_ptr anArc2 = std::dynamic_pointer_cast(aCirc2); + GCSPointPtr aPoint = + std::dynamic_pointer_cast(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)); }