+bool SketchSolver_ConstraintTangent::remove()
+{
+ if (myAuxPoint) {
+ std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+ std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+ GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
+ if (myAuxParameters[0])
+ aParams.insert(myAuxParameters[0]->scalar());
+ if (myAuxParameters[1])
+ aParams.insert(myAuxParameters[1]->scalar());
+ aStorage->removeParameters(aParams);
+ myAuxPoint = EntityWrapperPtr();
+ myAuxParameters[0] = myAuxParameters[1] = ScalarWrapperPtr();
+ }
+ return SketchSolver_Constraint::remove();
+}
+
+
+
+
+// ================== 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::map<AttributePtr, FeaturePtr> aCoincidentPoints;
+ 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() ||
+ aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID())) {
+ aCoincidences.insert(aRef);
+ AttributeRefAttrPtr aRefAttrA = aRef->refattr(SketchPlugin_Constraint::ENTITY_A());
+ if (!aRefAttrA->isObject())
+ aCoincidentPoints[aRefAttrA->attr()] = aRef;
+ AttributeRefAttrPtr aRefAttrB = aRef->refattr(SketchPlugin_Constraint::ENTITY_B());
+ if (!aRefAttrB->isObject())
+ aCoincidentPoints[aRefAttrB->attr()] = 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);
+ else if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+ aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID())) {
+ for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+ AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+ if (aRefAttr && !aRefAttr->isObject()) {
+ std::map<AttributePtr, FeaturePtr>::iterator aFound =
+ aCoincidentPoints.find(aRefAttr->attr());
+ if (aFound != aCoincidentPoints.end()) {
+ aCoincidencesBetweenFeatures.insert(aRef);
+ aCoincidencesBetweenFeatures.insert(aFound->second);
+ }
+ }
+ }
+ }
+ }
+
+ return aCoincidencesBetweenFeatures;
+}
+
+std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+ std::set<FeaturePtr> aCoincidences = collectCoincidences(theFeature1, theFeature2);
+ // collect points only
+ std::map<FeaturePtr, std::set<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::ATTRIBUTE(i));
+ if (!aRefAttr || aRefAttr->isObject())
+ continue;
+
+ AttributePtr anAttr = aRefAttr->attr();
+ FeaturePtr anOwner = ModelAPI_Feature::feature(anAttr->owner());
+ if (anOwner == theFeature1 || anOwner == theFeature2) {
+ if (anAttr->id() == SketchPlugin_BSplineBase::POLES_ID()) {
+ AttributePoint2DArrayPtr aPoles =
+ std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(anAttr);
+
+ AttributeIntegerPtr anIndex;
+ if (anOwner->getKind() == SketchPlugin_BSpline::ID()) {
+ anIndex = (*aCIt)->integer(i == 0 ?
+ SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A() :
+ SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
+ }
+ if (anIndex) {
+ if (anIndex->value() == 0)
+ anAttr = anOwner->attribute(SketchPlugin_BSpline::START_ID());
+ else if (anIndex->value() + 1 == aPoles->size())
+ anAttr = anOwner->attribute(SketchPlugin_BSpline::END_ID());
+ if (anAttr)
+ aCoincidentPoints[anOwner].insert(anAttr);
+ }
+ }
+ else if (anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
+ anAttr->id() != SketchPlugin_Circle::CENTER_ID())
+ aCoincidentPoints[anOwner].insert(anAttr);
+ }
+ }
+ }
+
+ std::set<AttributePtr> aBoundaryPoints;
+ if (aCoincidentPoints.size() == 2) {
+ for (std::map<FeaturePtr, std::set<AttributePtr> >::iterator anIt = aCoincidentPoints.begin();
+ anIt != aCoincidentPoints.end(); ++anIt)
+ aBoundaryPoints.insert(anIt->second.begin(), anIt->second.end());
+ }
+ return aBoundaryPoints;
+}
+
+static std::set<AttributePtr> refsToFeatureAndResults(FeaturePtr theFeature)
+{
+ std::set<AttributePtr> aRefs = theFeature->data()->refsToMe();
+ const std::list<ResultPtr>& aResults = theFeature->results();
+ for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
+ anIt != aResults.end(); ++anIt) {
+ const std::set<AttributePtr>& aResRefs = (*anIt)->data()->refsToMe();
+ aRefs.insert(aResRefs.begin(), aResRefs.end());
+ }
+ return aRefs;
+}
+
+// collect all points coincident with the feature
+static std::set<AttributePtr> pointsOnFeature(FeaturePtr theFeature)
+{
+ std::set<AttributePtr> aPoints;
+
+ std::set<AttributePtr> aRefs = refsToFeatureAndResults(theFeature);
+ for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
+ FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
+ if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+ aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID() ||
+ aRef->getKind() == SketchPlugin_ConstraintMiddle::ID())) {
+ for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+ AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+ if (aRefAttr) {
+ AttributePtr anAttr = aRefAttr->attr();
+ if (anAttr && anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
+ anAttr->id() != SketchPlugin_Circle::CENTER_ID())
+ aPoints.insert(anAttr);
+ }
+ }
+ }
+ }
+ return aPoints;
+}
+
+std::set<AttributePtr> coincidentPoints(FeaturePtr theFeature1, FeaturePtr theFeature2)
+{
+ std::set<AttributePtr> aPointsOnF1 = pointsOnFeature(theFeature1);
+ std::set<AttributePtr> aPointsOnF2 = pointsOnFeature(theFeature2);
+
+ std::set<AttributePtr> aCommonPoints;
+ for (std::set<AttributePtr>::iterator anIt = aPointsOnF1.begin();
+ anIt != aPointsOnF1.end(); ++anIt)
+ if (aPointsOnF2.find(*anIt) != aPointsOnF2.end())
+ aCommonPoints.insert(*anIt);
+ return aCommonPoints;
+}
+
+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::Arc> anArc = std::dynamic_pointer_cast<GCS::Arc>(aCirc);
+
+ std::shared_ptr<GCS::Line> aLine =
+ std::dynamic_pointer_cast<GCS::Line>(anEntLine->entity());
+
+ GCSConstraintPtr aNewConstr;
+ if (theSharedPoint && anArc) { // do not process shared point between circle and line
+ 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 createCurveCurveTangency(EntityWrapperPtr theEntity1,
+ EntityWrapperPtr theEntity2,
+ bool theInternalTangency,
+ EntityWrapperPtr theSharedPoint,
+ double* theAngle)
+{
+ GCSCurvePtr aCurve1 =
+ std::dynamic_pointer_cast<GCS::Curve>(GCS_EDGE_WRAPPER(theEntity1)->entity());
+ GCSCurvePtr aCurve2 =
+ std::dynamic_pointer_cast<GCS::Curve>(GCS_EDGE_WRAPPER(theEntity2)->entity());
+
+ GCSConstraintPtr aNewConstr;
+ if (theSharedPoint) {
+ GCSPointPtr aPoint =
+ std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theSharedPoint)->point();
+
+ adjustAngleBetweenCurves(aCurve1, aCurve2, aPoint, theAngle);
+ aNewConstr =
+ GCSConstraintPtr(new GCS::ConstraintAngleViaPoint(*aCurve1, *aCurve2, *aPoint, theAngle));
+ } else {
+ std::shared_ptr<GCS::Circle> aCirc1 = std::dynamic_pointer_cast<GCS::Circle>(aCurve1);
+ std::shared_ptr<GCS::Circle> aCirc2 = std::dynamic_pointer_cast<GCS::Circle>(aCurve2);
+ aNewConstr = GCSConstraintPtr(new GCS::ConstraintTangentCircumf(aCirc1->center, aCirc2->center,
+ aCirc1->rad, aCirc2->rad, theInternalTangency));
+ }
+
+ return ConstraintWrapperPtr(
+ new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_TANGENT_CURVE_CURVE));
+}
+
+void calculateTangencyPoint(EntityWrapperPtr theCurve1, EntityWrapperPtr theCurve2,
+ GCSPointPtr& theTangencyPoint)
+{
+ std::shared_ptr<GeomAPI_Ellipse2d> anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve1);
+ EntityWrapperPtr aCurve2 = theCurve2;
+ if (!anEllipse) {
+ // try converting to ellipse the second curve
+ anEllipse = PlaneGCSSolver_Tools::ellipse(theCurve2);
+ if (!anEllipse)
+ return; // no one curve is ellipse
+ aCurve2 = theCurve1;
+ }
+
+ GeomPnt2dPtr aP1, aP2;
+ if (aCurve2->type() == ENTITY_LINE) {
+ std::shared_ptr<GeomAPI_Lin2d> aLine = PlaneGCSSolver_Tools::line(aCurve2);
+ anEllipse->distance(aLine, aP1, aP2);
+ }
+ else if (aCurve2->type() == ENTITY_ARC || aCurve2->type() == ENTITY_CIRCLE) {
+ std::shared_ptr<GeomAPI_Circ2d> aCircle = PlaneGCSSolver_Tools::circle(aCurve2);
+ anEllipse->distance(aCircle, aP1, aP2);
+ }
+ else if (aCurve2->type() == ENTITY_ELLIPSE || aCurve2->type() == ENTITY_ELLIPTIC_ARC) {
+ std::shared_ptr<GeomAPI_Ellipse2d> anEl2 = PlaneGCSSolver_Tools::ellipse(aCurve2);
+ anEllipse->distance(anEl2, aP1, aP2);
+ }
+
+ if (aP1 && aP2) {
+ *theTangencyPoint->x = 0.5 * (aP1->x() + aP2->x());
+ *theTangencyPoint->y = 0.5 * (aP1->y() + aP2->y());
+ }
+}