X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchPlugin%2FSketchPlugin_Fillet.cpp;h=0acd530fd00d4400f6bffead813c1ce9faece2e0;hb=86f4ec4eb5ff5f119914f3d44cf945b49845d8c2;hp=f56b27d73e8c0a7a6a79dd9a8e94196b55f0c603;hpb=7470f1b70a31ce0483e168395a43db9c9c93589e;p=modules%2Fshaper.git diff --git a/src/SketchPlugin/SketchPlugin_Fillet.cpp b/src/SketchPlugin/SketchPlugin_Fillet.cpp index f56b27d73..0acd530fd 100644 --- a/src/SketchPlugin/SketchPlugin_Fillet.cpp +++ b/src/SketchPlugin/SketchPlugin_Fillet.cpp @@ -1,8 +1,21 @@ -// Copyright (C) 2014-20xx CEA/DEN, EDF R&D --> - -// File: SketchPlugin_Fillet.cpp -// Created: 19 Mar 2015 -// Author: Artem ZHIDKOV +// Copyright (C) 2014-2023 CEA, EDF +// +// 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 "SketchPlugin_Fillet.h" @@ -10,13 +23,18 @@ #include "SketchPlugin_Line.h" #include "SketchPlugin_Point.h" #include "SketchPlugin_Sketch.h" +#include "SketchPlugin_ConstraintDistance.h" #include "SketchPlugin_ConstraintEqual.h" #include "SketchPlugin_ConstraintCoincidence.h" #include "SketchPlugin_ConstraintLength.h" +#include "SketchPlugin_ConstraintMiddle.h" #include "SketchPlugin_ConstraintTangent.h" #include "SketchPlugin_ConstraintRadius.h" #include "SketchPlugin_Tools.h" +#include "SketchPlugin_Validators.h" +#include +#include #include #include #include @@ -24,6 +42,7 @@ #include #include +#include #include #include @@ -35,41 +54,38 @@ #include #include +#include #include const double tolerance = 1.e-7; const double paramTolerance = 1.e-4; +const double PI = 3.141592653589793238463; /// \brief Attract specified point on theNewArc to the attribute of theFeature static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute, FeaturePtr theFeature, const std::string& theFeatureAttribute); + +/// \brief Calculate radius of a fillet. +/// It should not be greater than 1/3 of shortest edge length. +static double calculateFilletRadius(FeaturePtr theFilletFeatures[2]); + /// \brief Calculates center of fillet arc and coordinates of tangency points -static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB, - double theRadius, bool theNotInversed[2], +static void calculateFilletCenter(FeaturePtr theFilletFeatures[2], + double theFilletRadius, + const std::shared_ptr& theSketchPlane, std::shared_ptr& theCenter, std::shared_ptr& theTangentA, std::shared_ptr& theTangentB); -/// Get point on 1/3 length of edge from fillet point -static void getPointOnEdge(const FeaturePtr theFeature, - const std::shared_ptr theFilletPoint, - std::shared_ptr& thePoint); - -/// Get distance from point to feature -static double getProjectionDistance(const FeaturePtr theFeature, - const std::shared_ptr thePoint); - -/// Get coincide edges for fillet -static std::set getCoincides(const FeaturePtr& theConstraintCoincidence); - static std::set findFeaturesToRemove(const FeaturePtr theFeature, const AttributePtr theAttribute); SketchPlugin_Fillet::SketchPlugin_Fillet() -: myFilletCreated(false) +: myIsReversed(false), myFilletCreated(false) { + myIsNotInversed[0] = myIsNotInversed[1] = true; } void SketchPlugin_Fillet::initAttributes() @@ -85,102 +101,68 @@ void SketchPlugin_Fillet::execute() if (isUpdateFlushed) Events_Loop::loop()->setFlushed(anUpdateEvent, false); - // Calculate Fillet parameters if does not yet - if (!myBaseFeatures[0] || !myBaseFeatures[1]) - calculateFilletParameters(); + // set flag here to avoid building Fillet presentation if "Redisplay" event appears + myFilletCreated = true; - // Create arc feature. - FeaturePtr aFilletArc = sketch()->addFeature(SketchPlugin_Arc::ID()); + // create feature for fillet arc + FeaturePtr aFilletArc = createFilletArc(); + if (!aFilletArc) { + setError("Error: unable to create a fillet arc."); + return; + } - // Set arc attributes. - bool aWasBlocked = aFilletArc->data()->blockSendAttributeUpdated(true); - std::dynamic_pointer_cast( - aFilletArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(myCenterXY->x(), - myCenterXY->y()); - std::shared_ptr aStartPoint = - std::dynamic_pointer_cast( - aFilletArc->attribute(SketchPlugin_Arc::START_ID())); - std::shared_ptr aEndPoint = - std::dynamic_pointer_cast( - aFilletArc->attribute(SketchPlugin_Arc::END_ID())); - if(aStartPoint->isInitialized() && aEndPoint->isInitialized() - && (aStartPoint->pnt()->xy()->distance(myTangentXY1) > tolerance - || aEndPoint->pnt()->xy()->distance(myTangentXY2) > tolerance)) { - std::dynamic_pointer_cast(aFilletArc)->setReversed(false); + // collect features referred to the edges participating in fillet + AttributePoint2DPtr aFilletPoints[2]; + int aFeatInd[2]; + int anAttrInd[2]; + std::set aFeaturesToBeRemoved; + for (int i = 0; i < 2; ++i) { + bool isFirstIndex = (i == 0); + aFeatInd[i] = myIsReversed == isFirstIndex ? 1 : 0; + anAttrInd[i] = (myIsReversed == isFirstIndex ? 2 : 0) + (myIsNotInversed[aFeatInd[i]] ? 0 : 1); + aFilletPoints[i] = std::dynamic_pointer_cast( + myBaseFeatures[aFeatInd[i]]->attribute(myFeatAttributes[anAttrInd[i]])); + std::set aRemove = + findFeaturesToRemove(myBaseFeatures[aFeatInd[i]], aFilletPoints[i]); + aFeaturesToBeRemoved.insert(aRemove.begin(), aRemove.end()); } - aStartPoint->setValue(myTangentXY1->x(), myTangentXY1->y()); - aEndPoint->setValue(myTangentXY2->x(), myTangentXY2->y()); - aFilletArc->data()->blockSendAttributeUpdated(aWasBlocked); - aFilletArc->execute(); - // Delete features with refs to points of edges. - std::shared_ptr aStartPoint1; - int aFeatInd1 = myIsReversed ? 1 : 0; - int anAttrInd1 = (myIsReversed ? 2 : 0) + (myIsNotInversed[aFeatInd1] ? 0 : 1); - aStartPoint1 = std::dynamic_pointer_cast( - myBaseFeatures[aFeatInd1]->attribute(myFeatAttributes[anAttrInd1])); - std::set aFeaturesToBeRemoved1 = - findFeaturesToRemove(myBaseFeatures[aFeatInd1], aStartPoint1); - - std::shared_ptr aStartPoint2; - int aFeatInd2 = myIsReversed ? 0 : 1; - int anAttrInd2 = (myIsReversed ? 0 : 2) + (myIsNotInversed[aFeatInd2] ? 0 : 1); - aStartPoint2 = std::dynamic_pointer_cast( - myBaseFeatures[aFeatInd2]->attribute(myFeatAttributes[anAttrInd2])); - std::set aFeaturesToBeRemoved2 = - findFeaturesToRemove(myBaseFeatures[aFeatInd2], aStartPoint2); - - aFeaturesToBeRemoved1.insert(aFeaturesToBeRemoved2.begin(), aFeaturesToBeRemoved2.end()); - ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved1); - Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); + // keep "distance" constraints and remove all other references + removeReferencesButKeepDistances(aFeaturesToBeRemoved, aFilletPoints); // Update fillet edges. recalculateAttributes(aFilletArc, SketchPlugin_Arc::START_ID(), - myBaseFeatures[aFeatInd1], myFeatAttributes[anAttrInd1]); + myBaseFeatures[aFeatInd[0]], myFeatAttributes[anAttrInd[0]]); recalculateAttributes(aFilletArc, SketchPlugin_Arc::END_ID(), - myBaseFeatures[aFeatInd2], myFeatAttributes[anAttrInd2]); + myBaseFeatures[aFeatInd[1]], myFeatAttributes[anAttrInd[1]]); + + FeaturePtr aConstraint; // Create coincidence features. - FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); - AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setAttr(aFilletArc->attribute(SketchPlugin_Arc::START_ID())); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttr->setAttr(myBaseFeatures[aFeatInd1]->attribute(myFeatAttributes[anAttrInd1])); - aConstraint->execute(); + aConstraint = SketchPlugin_Tools::createConstraintAttrAttr(sketch(), + SketchPlugin_ConstraintCoincidence::ID(), + aFilletArc->attribute(SketchPlugin_Arc::START_ID()), + myBaseFeatures[aFeatInd[0]]->attribute(myFeatAttributes[anAttrInd[0]])); ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); - aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setAttr(aFilletArc->attribute(SketchPlugin_Arc::END_ID())); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttr->setAttr(myBaseFeatures[aFeatInd2]->attribute(myFeatAttributes[anAttrInd2])); - aConstraint->execute(); + aConstraint = SketchPlugin_Tools::createConstraintAttrAttr(sketch(), + SketchPlugin_ConstraintCoincidence::ID(), + aFilletArc->attribute(SketchPlugin_Arc::END_ID()), + myBaseFeatures[aFeatInd[1]]->attribute(myFeatAttributes[anAttrInd[1]])); ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); // Create tangent features. for (int i = 0; i < 2; i++) { - aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID()); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setObject(aFilletArc->lastResult()); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - bool isArc = myBaseFeatures[i]->getKind() == SketchPlugin_Arc::ID(); - aRefAttr->setObject(isArc ? myBaseFeatures[i]->lastResult() : - myBaseFeatures[i]->firstResult()); + aConstraint = SketchPlugin_Tools::createConstraintObjectObject(sketch(), + SketchPlugin_ConstraintTangent::ID(), + aFilletArc->lastResult(), + myBaseFeatures[i]->lastResult()); aConstraint->execute(); ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); } // Send events to update the sub-features by the solver. - if(isUpdateFlushed) { + if (isUpdateFlushed) Events_Loop::loop()->setFlushed(anUpdateEvent, true); - } - - myFilletCreated = true; } AISObjectPtr SketchPlugin_Fillet::getAISObject(AISObjectPtr thePrevious) @@ -213,6 +195,11 @@ AISObjectPtr SketchPlugin_Fillet::getAISObject(AISObjectPtr thePrevious) anAISObject = AISObjectPtr(new GeomAPI_AISObject); } anAISObject->createShape(anArcShape); + bool isAxiliary = false; + AttributeBooleanPtr aAttr = boolean(AUXILIARY_ID()); + if (aAttr.get()) + isAxiliary = aAttr->value(); + SketchPlugin_Tools::customizeFeaturePrs(anAISObject, isAxiliary); return anAISObject; } @@ -227,23 +214,18 @@ bool SketchPlugin_Fillet::calculateFilletParameters() if (!aFilletPoint2D.get()) return false; - if (!findFeaturesContainingFilletPoint(aFilletPoint2D)) { - setError("Error: Selected point does not have two suitable edges for fillet."); + Events_InfoMessage anError; + FeaturePtr anEdge1, anEdge2; + if (!SketchPlugin_FilletVertexValidator::isValidVertex + (aPointRefAttr, anError, anEdge1, anEdge2)) { + setError(anError.messageString()); return false; } + myBaseFeatures[0] = anEdge1; + myBaseFeatures[1] = anEdge2; - // Getting points located at 1/3 of edge length from fillet point. std::shared_ptr aFilletPnt2d = aFilletPoint2D->pnt(); - std::shared_ptr aPnt1, aPnt2; - getPointOnEdge(myBaseFeatures[0], aFilletPnt2d, aPnt1); - getPointOnEdge(myBaseFeatures[1], aFilletPnt2d, aPnt2); - - /// Getting distances. - double aDistance1 = getProjectionDistance(myBaseFeatures[1], aPnt1); - double aDistance2 = getProjectionDistance(myBaseFeatures[0], aPnt2); - - // Calculate radius value for fillet. - double aRadius = aDistance1 < aDistance2 ? aDistance1 / 2.0 : aDistance2 / 2.0; + double aRadius = calculateFilletRadius(myBaseFeatures); // Calculate arc attributes. static const int aNbFeatures = 2; @@ -276,8 +258,9 @@ bool SketchPlugin_Fillet::calculateFilletParameters() } } - calculateFilletCenter(myBaseFeatures[0], myBaseFeatures[1], aRadius, - myIsNotInversed, myCenterXY, myTangentXY1, myTangentXY2); + std::shared_ptr aSketchPlane = SketchPlugin_Sketch::plane(sketch()); + calculateFilletCenter(myBaseFeatures, aRadius, aSketchPlane, + myCenterXY, myTangentXY1, myTangentXY2); // Tangent directions of the features in coincident point. std::shared_ptr aTangentDir[aNbFeatures]; @@ -320,49 +303,180 @@ bool SketchPlugin_Fillet::calculateFilletParameters() return true; } -bool SketchPlugin_Fillet::findFeaturesContainingFilletPoint( - std::shared_ptr theFilletPoint) +FeaturePtr SketchPlugin_Fillet::createFilletArc() { - // Obtain constraint coincidence for the fillet point. - FeaturePtr aConstraintCoincidence; - const std::set& aRefsList = theFilletPoint->owner()->data()->refsToMe(); - for(std::set::const_iterator anIt = aRefsList.cbegin(); - anIt != aRefsList.cend(); - ++anIt) { - std::shared_ptr anAttr = (*anIt); - FeaturePtr aFeature = std::dynamic_pointer_cast(anAttr->owner()); - if(aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { - AttributeRefAttrPtr anAttrRefA = - aFeature->refattr(SketchPlugin_ConstraintCoincidence::ENTITY_A()); - AttributeRefAttrPtr anAttrRefB = - aFeature->refattr(SketchPlugin_ConstraintCoincidence::ENTITY_B()); - if(anAttrRefA.get() && !anAttrRefA->isObject()) { - AttributePtr anAttrA = anAttrRefA->attr(); - if(theFilletPoint == anAttrA) { - aConstraintCoincidence = aFeature; - break; + // Calculate Fillet parameters if does not yet + if (!myBaseFeatures[0] || !myBaseFeatures[1]) + calculateFilletParameters(); + + // fix for issue #2810 (sometimes, myCenterXY is NULL, fillet should report an error) + if (!myCenterXY) + return FeaturePtr(); + + // Create arc feature. + FeaturePtr aFilletArc = sketch()->addFeature(SketchPlugin_Arc::ID()); + + // Set arc attributes. + bool aWasBlocked = aFilletArc->data()->blockSendAttributeUpdated(true); + std::dynamic_pointer_cast( + aFilletArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(myCenterXY->x(), + myCenterXY->y()); + std::shared_ptr aStartPoint = + std::dynamic_pointer_cast( + aFilletArc->attribute(SketchPlugin_Arc::START_ID())); + std::shared_ptr aEndPoint = + std::dynamic_pointer_cast( + aFilletArc->attribute(SketchPlugin_Arc::END_ID())); + if(aStartPoint->isInitialized() && aEndPoint->isInitialized() + && (aStartPoint->pnt()->xy()->distance(myTangentXY1) > tolerance + || aEndPoint->pnt()->xy()->distance(myTangentXY2) > tolerance)) { + std::dynamic_pointer_cast(aFilletArc)->setReversed(false); + } + aStartPoint->setValue(myTangentXY1->x(), myTangentXY1->y()); + aEndPoint->setValue(myTangentXY2->x(), myTangentXY2->y()); + aFilletArc->data()->blockSendAttributeUpdated(aWasBlocked); + aFilletArc->execute(); + + return aFilletArc; +} + +FeaturePtr SketchPlugin_Fillet::createFilletApex(const GeomPnt2dPtr& theCoordinates) +{ + FeaturePtr anApex = sketch()->addFeature(SketchPlugin_Point::ID()); + AttributePoint2DPtr aCoord = std::dynamic_pointer_cast( + anApex->attribute(SketchPlugin_Point::COORD_ID())); + aCoord->setValue(theCoordinates); + anApex->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true); + + // additional coincidence constraints + static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); + FeaturePtr aConstraint; + for (int i = 0; i < 2; i++) { + aConstraint = SketchPlugin_Tools::createConstraintAttrObject(sketch(), + SketchPlugin_ConstraintCoincidence::ID(), + aCoord, + myBaseFeatures[i]->lastResult()); + aConstraint->execute(); + ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + } + + return anApex; +} + +struct Length { + AttributePtr myPoints[2]; + std::wstring myValueText; + double myValueDouble; + GeomPnt2dPtr myFlyoutPoint; + int myLocationType; +}; + +void SketchPlugin_Fillet::removeReferencesButKeepDistances( + std::set& theFeaturesToRemove, + const AttributePoint2DPtr theFilletPoints[2]) +{ + FeaturePtr aFilletApex; + std::list aLengthToDistance; + + std::set::iterator aFeat = theFeaturesToRemove.begin(); + while (aFeat != theFeaturesToRemove.end()) { + std::shared_ptr aDistance = + std::dynamic_pointer_cast(*aFeat); + if (aDistance) { + if (!aFilletApex) + aFilletApex = createFilletApex(theFilletPoints[0]->pnt()); + // update attributes of distance constraints + bool isUpdated = false; + for (int attrInd = 0; attrInd < CONSTRAINT_ATTR_SIZE && !isUpdated; ++attrInd) { + AttributeRefAttrPtr aRefAttr = + aDistance->refattr(SketchPlugin_Constraint::ATTRIBUTE(attrInd)); + if (aRefAttr && !aRefAttr->isObject() && + (aRefAttr->attr() == theFilletPoints[0] || aRefAttr->attr() == theFilletPoints[1])) { + aRefAttr->setAttr(aFilletApex->attribute(SketchPlugin_Point::COORD_ID())); + isUpdated = true; } } - if(anAttrRefB.get() && !anAttrRefB->isObject()) { - AttributePtr anAttrB = anAttrRefB->attr(); - if(theFilletPoint == anAttrB) { - aConstraintCoincidence = aFeature; - break; + // avoid distance from removing if it is updated + std::set::iterator aKeepIt = aFeat++; + if (isUpdated) + theFeaturesToRemove.erase(aKeepIt); + + } else { + std::shared_ptr aLength = + std::dynamic_pointer_cast(*aFeat); + if (aLength) { + if (!aFilletApex) + aFilletApex = createFilletApex(theFilletPoints[0]->pnt()); + // remove Length, but create new distance constraint + AttributeRefAttrPtr aRefAttr = + aLength->refattr(SketchPlugin_Constraint::ENTITY_A()); + FeaturePtr aLine = ModelAPI_Feature::feature(aRefAttr->object()); + if (aLine) { + aLengthToDistance.push_back(Length()); + Length& aNewLength = aLengthToDistance.back(); + // main attrbutes + for (int i = 0; i < 2; ++i) { + AttributePtr anAttr = aLine->attribute( + i == 0 ? SketchPlugin_Line::START_ID() : SketchPlugin_Line::END_ID()); + if (anAttr == theFilletPoints[0] || anAttr == theFilletPoints[1]) + aNewLength.myPoints[i] = aFilletApex->attribute(SketchPlugin_Point::COORD_ID()); + else + aNewLength.myPoints[i] = anAttr; + } + // value + AttributeDoublePtr aValue = aLength->real(SketchPlugin_Constraint::VALUE()); + aNewLength.myValueDouble = aValue->value(); + aNewLength.myValueText = aValue->text(); + // auxiliary attributes + AttributePoint2DPtr aFlyoutAttr = std::dynamic_pointer_cast( + aLength->attribute(SketchPlugin_ConstraintLength::FLYOUT_VALUE_PNT())); + if (aFlyoutAttr && aFlyoutAttr->isInitialized()) + aNewLength.myFlyoutPoint = SketchPlugin_Tools::flyoutPointCoordinates(aLength); + AttributeIntegerPtr aLocationAttr = + aLength->integer(SketchPlugin_ConstraintLength::LOCATION_TYPE_ID()); + if (aLocationAttr && aLocationAttr->isInitialized()) + aNewLength.myLocationType = aLocationAttr->value(); + else + aNewLength.myLocationType = -1; } } + + ++aFeat; } } - if(!aConstraintCoincidence.get()) - return false; - - // Get coincide edges. - std::set anEdgeFeatures = getCoincides(aConstraintCoincidence); - std::set::iterator aLinesIt = anEdgeFeatures.begin(); - for (int i = 0; aLinesIt != anEdgeFeatures.end() && i < 2; ++aLinesIt, ++i) - myBaseFeatures[i] = *aLinesIt; + // remove references + ModelAPI_Tools::removeFeaturesAndReferences(theFeaturesToRemove); + Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED)); - return myBaseFeatures[0] && myBaseFeatures[1]; + // restore Length constraints as point-point distances + FeaturePtr aConstraint; + std::list::iterator anIt = aLengthToDistance.begin(); + for (; anIt != aLengthToDistance.end(); ++anIt) { + aConstraint = SketchPlugin_Tools::createConstraintAttrAttr(sketch(), + SketchPlugin_ConstraintDistance::ID(), anIt->myPoints[0], anIt->myPoints[1]); + // set value + AttributeDoublePtr aValue = aConstraint->real(SketchPlugin_Constraint::VALUE()); + if (anIt->myValueText.empty()) + aValue->setValue(anIt->myValueDouble); + else + aValue->setText(anIt->myValueText); + // set flyout point if exists + if (anIt->myFlyoutPoint) { + AttributePoint2DPtr aFlyoutAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_ConstraintDistance::FLYOUT_VALUE_PNT())); + aFlyoutAttr->setValue(anIt->myFlyoutPoint); + } + // set location type if initialized + if (anIt->myLocationType >= 0) { + AttributeIntegerPtr aLocationType = + aConstraint->integer(SketchPlugin_ConstraintDistance::LOCATION_TYPE_ID()); + aLocationType->setValue(anIt->myLocationType); + } + aConstraint->execute(); + ModelAPI_EventCreator::get()->sendUpdated(aConstraint, + Events_Loop::eventByName(EVENT_OBJECT_UPDATED)); + } } // ========= Auxiliary functions ================= @@ -375,395 +489,88 @@ void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAt theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y()); } -/// \brief Find intersections of lines shifted along normal direction -void possibleFilletCenterLineLine( - std::shared_ptr thePointA, std::shared_ptr theDirA, - std::shared_ptr thePointB, std::shared_ptr theDirB, - double theRadius, std::list< std::shared_ptr >& theCenters) +static std::shared_ptr toPoint(const AttributePtr& theAttribute) { - std::shared_ptr aDirAT(new GeomAPI_Dir2d(-theDirA->y(), theDirA->x())); - std::shared_ptr aDirBT(new GeomAPI_Dir2d(-theDirB->y(), theDirB->x())); - std::shared_ptr aPntA, aPntB; - double aDet = theDirA->cross(theDirB); - for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) { - aPntA = thePointA->added(aDirAT->xy()->multiplied(aStepA * theRadius)); - for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) { - aPntB = thePointB->added(aDirBT->xy()->multiplied(aStepB * theRadius)); - double aVX = aDirAT->xy()->dot(aPntA); - double aVY = aDirBT->xy()->dot(aPntB); - std::shared_ptr aPoint(new GeomAPI_XY( - (theDirB->x() * aVX - theDirA->x() * aVY) / aDet, - (theDirB->y() * aVX - theDirA->y() * aVY) / aDet)); - theCenters.push_back(aPoint); - } - } + std::shared_ptr aPoint; + AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast(theAttribute); + if (aPointAttr) + aPoint = aPointAttr->pnt(); + return aPoint; } -/// \brief Find intersections of line shifted along normal direction in both sides -/// and a circle with extended radius -void possibleFilletCenterLineArc( - std::shared_ptr theStartLine, std::shared_ptr theDirLine, - std::shared_ptr theCenterArc, double theRadiusArc, - double theRadius, std::list< std::shared_ptr >& theCenters) +static std::shared_ptr toLine(const FeaturePtr& theFeature) { - std::shared_ptr aDirT(new GeomAPI_Dir2d(-theDirLine->y(), theDirLine->x())); - std::shared_ptr aPnt; - double aDirNorm2 = theDirLine->dot(theDirLine); - double aRad = 0.0; - double aDirX = theDirLine->x(); - double aDirX2 = theDirLine->x() * theDirLine->x(); - double aDirY2 = theDirLine->y() * theDirLine->y(); - double aDirXY = theDirLine->x() * theDirLine->y(); - for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) { - aPnt = theStartLine->added(aDirT->xy()->multiplied(aStepA * theRadius)); - double aCoeff = aDirT->xy()->dot(aPnt->decreased(theCenterArc)); - double aCoeff2 = aCoeff * aCoeff; - for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) { - aRad = theRadiusArc + aStepB * theRadius; - double aD = aRad * aRad * aDirNorm2 - aCoeff2; - if (aD < 0.0) - continue; - double aDs = sqrt(aD); - double x1 = theCenterArc->x() + (aCoeff * aDirT->x() - aDirT->y() * aDs) / aDirNorm2; - double x2 = theCenterArc->x() + (aCoeff * aDirT->x() + aDirT->y() * aDs) / aDirNorm2; - double y1 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() - - aDirXY * (aPnt->x() - theCenterArc->x()) - theDirLine->y() * aDs) / aDirNorm2; - double y2 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() - - aDirXY * (aPnt->x() - theCenterArc->x()) + theDirLine->y() * aDs) / aDirNorm2; - - std::shared_ptr aPoint1(new GeomAPI_XY(x1, y1)); - theCenters.push_back(aPoint1); - std::shared_ptr aPoint2(new GeomAPI_XY(x2, y2)); - theCenters.push_back(aPoint2); - } + std::shared_ptr aLine; + if (theFeature->getKind() == SketchPlugin_Line::ID()) { + std::shared_ptr aStart = + toPoint( theFeature->attribute(SketchPlugin_Line::START_ID()) ); + std::shared_ptr aEnd = + toPoint( theFeature->attribute(SketchPlugin_Line::END_ID()) ); + aLine = std::shared_ptr(new GeomAPI_Lin2d(aStart, aEnd)); } + return aLine; } -/// \brief Find intersections of two circles with extended radii -void possibleFilletCenterArcArc( - std::shared_ptr theCenterA, double theRadiusA, - std::shared_ptr theCenterB, double theRadiusB, - double theRadius, std::list< std::shared_ptr >& theCenters) +static std::shared_ptr toCircle(const FeaturePtr& theFeature) { - std::shared_ptr aCenterDir = theCenterB->decreased(theCenterA); - double aCenterDist2 = aCenterDir->dot(aCenterDir); - double aCenterDist = sqrt(aCenterDist2); - - double aRadA, aRadB; - for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) { - aRadA = theRadiusA + aStepA * theRadius; - for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) { - aRadB = theRadiusB + aStepB * theRadius; - if (aRadA + aRadB < aCenterDist || fabs(aRadA - aRadB) > aCenterDist) - continue; // there is no intersections - - double aMedDist = (aRadA * aRadA - aRadB * aRadB + aCenterDist2) / (2.0 * aCenterDist); - double aHeight = sqrt(aRadA * aRadA - aMedDist * aMedDist); - - double x1 = theCenterA->x() + - (aMedDist * aCenterDir->x() + aCenterDir->y() * aHeight) / aCenterDist; - double y1 = theCenterA->y() + - (aMedDist * aCenterDir->y() - aCenterDir->x() * aHeight) / aCenterDist; - - double x2 = theCenterA->x() + - (aMedDist * aCenterDir->x() - aCenterDir->y() * aHeight) / aCenterDist; - double y2 = theCenterA->y() + - (aMedDist * aCenterDir->y() + aCenterDir->x() * aHeight) / aCenterDist; - - std::shared_ptr aPoint1(new GeomAPI_XY(x1, y1)); - theCenters.push_back(aPoint1); - std::shared_ptr aPoint2(new GeomAPI_XY(x2, y2)); - theCenters.push_back(aPoint2); - } + std::shared_ptr aCircle; + if (theFeature->getKind() == SketchPlugin_Arc::ID()) { + std::shared_ptr aCenter = + toPoint( theFeature->attribute(SketchPlugin_Arc::CENTER_ID()) ); + std::shared_ptr aStart = + toPoint( theFeature->attribute(SketchPlugin_Arc::START_ID()) ); + aCircle = std::shared_ptr(new GeomAPI_Circ2d(aCenter, aStart)); } + return aCircle; } -void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB, - double theRadius, bool theNotInversed[2], + +void calculateFilletCenter(FeaturePtr theFilletFeatures[2], + double theFilletRadius, + const std::shared_ptr& theSketchPlane, std::shared_ptr& theCenter, std::shared_ptr& theTangentA, std::shared_ptr& theTangentB) { - static const int aNbFeatures = 2; - FeaturePtr aFeature[aNbFeatures] = {theFeatureA, theFeatureB}; - std::shared_ptr aStart[aNbFeatures], aEnd[aNbFeatures], aCenter[aNbFeatures]; - std::shared_ptr aStartPoint, aEndPoint; - - for (int i = 0; i < aNbFeatures; i++) { - if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) { - aStartPoint = std::dynamic_pointer_cast( - aFeature[i]->attribute(SketchPlugin_Line::START_ID())); - aEndPoint = std::dynamic_pointer_cast( - aFeature[i]->attribute(SketchPlugin_Line::END_ID())); - } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) { - aStartPoint = std::dynamic_pointer_cast( - aFeature[i]->attribute(SketchPlugin_Arc::START_ID())); - aEndPoint = std::dynamic_pointer_cast( - aFeature[i]->attribute(SketchPlugin_Arc::END_ID())); - aCenter[i] = std::dynamic_pointer_cast( - aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt()->xy(); - } else - return; - aStart[i] = std::shared_ptr(theNotInversed[i] ? - new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()) : - new GeomAPI_XY(aEndPoint->x(), aEndPoint->y())); - aEnd[i] = std::shared_ptr(theNotInversed[i] ? - new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()) : - new GeomAPI_XY(aStartPoint->x(), aStartPoint->y())); - } - - if (theFeatureA->getKind() == SketchPlugin_Line::ID() && - theFeatureB->getKind() == SketchPlugin_Line::ID()) { - std::shared_ptr aDir[2]; - std::shared_ptr aDirT[2]; - for (int i = 0; i < aNbFeatures; i++) { - aDir[i] = std::shared_ptr(new GeomAPI_Dir2d(aEnd[i]->decreased(aStart[i]))); - aDirT[i] = std::shared_ptr(new GeomAPI_Dir2d(-aDir[i]->y(), aDir[i]->x())); - } - - // get and filter possible centers - std::list< std::shared_ptr > aSuspectCenters; - possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1], - theRadius, aSuspectCenters); - double aDot = 0.0; - std::list< std::shared_ptr >::iterator anIt = aSuspectCenters.begin(); - for (; anIt != aSuspectCenters.end(); anIt++) { - aDot = aDirT[0]->xy()->dot(aStart[0]->decreased(*anIt)); - theTangentA = (*anIt)->added(aDirT[0]->xy()->multiplied(aDot)); - if (theTangentA->decreased(aStart[0])->dot(aDir[0]->xy()) < 0.0) - continue; // incorrect position - aDot = aDirT[1]->xy()->dot(aStart[1]->decreased(*anIt)); - theTangentB = (*anIt)->added(aDirT[1]->xy()->multiplied(aDot)); - if (theTangentB->decreased(aStart[1])->dot(aDir[1]->xy()) < 0.0) - continue; // incorrect position - // the center is found, stop searching - theCenter = *anIt; - return; - } - } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() && - theFeatureB->getKind() == SketchPlugin_Line::ID()) || - (theFeatureA->getKind() == SketchPlugin_Line::ID() && - theFeatureB->getKind() == SketchPlugin_Arc::ID())) { - int aLineInd = theFeatureA->getKind() == SketchPlugin_Line::ID() ? 0 : 1; - double anArcRadius = aStart[1-aLineInd]->distance(aCenter[1-aLineInd]); - std::shared_ptr aDirLine = std::shared_ptr( - new GeomAPI_Dir2d(aEnd[aLineInd]->decreased(aStart[aLineInd]))); - std::shared_ptr aDirT = std::shared_ptr( - new GeomAPI_Dir2d(-aDirLine->y(), aDirLine->x())); - - std::shared_ptr aStartArcDir = std::shared_ptr( - new GeomAPI_Dir2d(aStart[1-aLineInd]->decreased(aCenter[1-aLineInd]))); - std::shared_ptr aEndArcDir = std::shared_ptr( - new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd]))); - double anArcAngle = aEndArcDir->angle(aStartArcDir); - - // get possible centers and filter them - std::list< std::shared_ptr > aSuspectCenters; - possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], - anArcRadius, theRadius, aSuspectCenters); - double aDot = 0.0; - // the line is forward into the arc - double innerArc = aCenter[1-aLineInd]->decreased(aStart[aLineInd])->dot(aDirLine->xy()); - std::shared_ptr aLineTgPoint, anArcTgPoint; - // The possible centers are ranged by their positions. - // If the point is not satisfy one of criteria, the weight is decreased with penalty. - int aBestWeight = 0; - std::list< std::shared_ptr >::iterator anIt = aSuspectCenters.begin(); - for (; anIt != aSuspectCenters.end(); anIt++) { - int aWeight = 2; - aDot = aDirT->xy()->dot(aStart[aLineInd]->decreased(*anIt)); - aLineTgPoint = (*anIt)->added(aDirT->xy()->multiplied(aDot)); - // Check the point is placed on the correct arc (penalty if false) - if (aCenter[1-aLineInd]->distance(*anIt) * innerArc > anArcRadius * innerArc) - aWeight -= 1; - std::shared_ptr aCurDir = std::shared_ptr( - new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1-aLineInd]))); - double aCurAngle = aCurDir->angle(aStartArcDir); - if (anArcAngle < 0.0) aCurAngle *= -1.0; - if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle)) - continue; - if (aWeight > aBestWeight) - aBestWeight = aWeight; - else if (aWeight < aBestWeight || - aStart[aLineInd]->distance(*anIt) > - aStart[aLineInd]->distance(theCenter)) // <-- take closer point - continue; - // the center is found, stop searching - theCenter = *anIt; - anArcTgPoint = aCenter[1-aLineInd]->added(aCurDir->xy()->multiplied(anArcRadius)); - if (theFeatureA->getKind() == SketchPlugin_Line::ID()) { - theTangentA = aLineTgPoint; - theTangentB = anArcTgPoint; - } else { - theTangentA = anArcTgPoint; - theTangentB = aLineTgPoint; - } - //return; - } - } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() && - theFeatureB->getKind() == SketchPlugin_Arc::ID()) { - double anArcRadius[aNbFeatures]; - double anArcAngle[aNbFeatures]; - std::shared_ptr aStartArcDir[aNbFeatures]; - for (int i = 0; i < aNbFeatures; i++) { - anArcRadius[i] = aStart[i]->distance(aCenter[i]); - aStartArcDir[i] = std::shared_ptr( - new GeomAPI_Dir2d(aStart[i]->decreased(aCenter[i]))); - std::shared_ptr aEndArcDir = std::shared_ptr( - new GeomAPI_Dir2d(aEnd[i]->decreased(aCenter[i]))); - anArcAngle[i] = aEndArcDir->angle(aStartArcDir[i]); - } - - // get and filter possible centers - std::list< std::shared_ptr > aSuspectCenters; - possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1], - anArcRadius[1], theRadius, aSuspectCenters); - double aDot = 0.0; - std::shared_ptr aLineTgPoint, anArcTgPoint; - std::list< std::shared_ptr >::iterator anIt = aSuspectCenters.begin(); - for (; anIt != aSuspectCenters.end(); anIt++) { - std::shared_ptr aCurDir = std::shared_ptr( - new GeomAPI_Dir2d((*anIt)->decreased(aCenter[0]))); - double aCurAngle = aCurDir->angle(aStartArcDir[0]); - if (anArcAngle[0] < 0.0) aCurAngle *= -1.0; - if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[0])) - continue; // incorrect position - theTangentA = aCenter[0]->added(aCurDir->xy()->multiplied(anArcRadius[0])); - - aCurDir = std::shared_ptr(new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1]))); - aCurAngle = aCurDir->angle(aStartArcDir[1]); - if (anArcAngle[1] < 0.0) aCurAngle *= -1.0; - if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[1])) - continue; // incorrect position - theTangentB = aCenter[1]->added(aCurDir->xy()->multiplied(anArcRadius[1])); - - // the center is found, stop searching - theCenter = *anIt; - return; - } - } -} - -void getPointOnEdge(const FeaturePtr theFeature, - const std::shared_ptr theFilletPoint, - std::shared_ptr& thePoint) { - if(theFeature->getKind() == SketchPlugin_Line::ID()) { - std::shared_ptr aPntStart = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt(); - std::shared_ptr aPntEnd = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt(); - if(aPntStart->distance(theFilletPoint) > 1.e-7) { - aPntStart = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt(); - aPntEnd = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt(); - } - thePoint.reset( - new GeomAPI_Pnt2d(aPntStart->xy()->added( - aPntEnd->xy()->decreased( aPntStart->xy() )->multiplied(1.0 / 3.0) ) ) ); - } else { - std::shared_ptr aPntTemp; - std::shared_ptr aPntStart = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt(); - std::shared_ptr aPntEnd = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt(); - if(theFeature->attribute(SketchPlugin_Arc::INVERSED_ID())) { - aPntTemp = aPntStart; - aPntStart = aPntEnd; - aPntEnd = aPntTemp; + GeomShapePtr aShapeA = theFilletFeatures[0]->lastResult()->shape(); + GeomShapePtr aShapeB = theFilletFeatures[1]->lastResult()->shape(); + + GeomAlgoAPI_Circ2dBuilder aCircBuilder(theSketchPlane); + aCircBuilder.addTangentCurve(aShapeA); + aCircBuilder.addTangentCurve(aShapeB); + aCircBuilder.setRadius(theFilletRadius); + + std::shared_ptr aFilletCircle = aCircBuilder.circle(); + if (!aFilletCircle) + return; + + theCenter = aFilletCircle->center()->xy(); + // tangent points + std::shared_ptr aTgPoints[2]; + for (int i = 0; i < 2; ++i) { + std::shared_ptr aCircle = toCircle(theFilletFeatures[i]); + if (aCircle) + aTgPoints[i] = aCircle->project(aFilletCircle->center()); + else { + std::shared_ptr aLine = toLine(theFilletFeatures[i]); + if (aLine) + aTgPoints[i] = aLine->project(aFilletCircle->center()); } - std::shared_ptr aCenterPnt = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt(); - std::shared_ptr aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart)); - double aStartParameter(0), anEndParameter(0); - aCirc->parameter(aPntStart, paramTolerance, aStartParameter); - aCirc->parameter(aPntEnd, paramTolerance, anEndParameter); - if(aPntStart->distance(theFilletPoint) > tolerance) { - double aTmpParameter = aStartParameter; - aStartParameter = anEndParameter; - anEndParameter = aTmpParameter; - } - double aPntParameter = aStartParameter + (anEndParameter - aStartParameter) / 3.0; - aCirc->D0(aPntParameter, thePoint); - } -} - -double getProjectionDistance(const FeaturePtr theFeature, - const std::shared_ptr thePoint) -{ - std::shared_ptr aProjectPnt; - if(theFeature->getKind() == SketchPlugin_Line::ID()) { - std::shared_ptr aPntStart = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt(); - std::shared_ptr aPntEnd = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt(); - std::shared_ptr aLin(new GeomAPI_Lin2d(aPntStart, aPntEnd)); - aProjectPnt = aLin->project(thePoint); - } else { - std::shared_ptr aPntStart = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt(); - std::shared_ptr aPntEnd = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt(); - std::shared_ptr aCenterPnt = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt(); - std::shared_ptr aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart)); - aProjectPnt = aCirc->project(thePoint); - } - if(aProjectPnt.get()) { - return aProjectPnt->distance(thePoint); } - return -1; + theTangentA = aTgPoints[0]->xy(); + theTangentB = aTgPoints[1]->xy(); } -std::set getCoincides(const FeaturePtr& theConstraintCoincidence) +double calculateFilletRadius(FeaturePtr theFilletFeatures[2]) { - std::set aCoincides; - - std::shared_ptr aFilletPnt = - SketchPlugin_Tools::getCoincidencePoint(theConstraintCoincidence); - - SketchPlugin_Tools::findCoincidences(theConstraintCoincidence, - SketchPlugin_ConstraintCoincidence::ENTITY_A(), - aCoincides); - SketchPlugin_Tools::findCoincidences(theConstraintCoincidence, - SketchPlugin_ConstraintCoincidence::ENTITY_B(), - aCoincides); - - // Remove points from set of coincides. - std::set aNewSetOfCoincides; - for(std::set::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) { - std::shared_ptr aSketchEntity = - std::dynamic_pointer_cast(*anIt); - if(aSketchEntity.get() && aSketchEntity->isCopy()) { - continue; - } - if((*anIt)->getKind() == SketchPlugin_Line::ID()) { - aNewSetOfCoincides.insert(*anIt); - } else if((*anIt)->getKind() == SketchPlugin_Arc::ID()) { - AttributePtr anAttrCenter = (*anIt)->attribute(SketchPlugin_Arc::CENTER_ID()); - std::shared_ptr aPointCenter2D = - std::dynamic_pointer_cast(anAttrCenter); - if(aPointCenter2D.get() && aFilletPnt->isEqual(aPointCenter2D->pnt())) { - continue; - } - aNewSetOfCoincides.insert(*anIt); - } + double aLengths[2] = { 0, 0 }; + for (int i = 0; i < 2; ++i) { + GeomShapePtr aShape = theFilletFeatures[i]->lastResult()->shape(); + std::shared_ptr anEdge = std::dynamic_pointer_cast(aShape); + if (anEdge) + aLengths[i] = anEdge->length(); } - aCoincides = aNewSetOfCoincides; - - // If we still have more than two coincides remove auxilary entities from set of coincides. - if(aCoincides.size() > 2) { - aNewSetOfCoincides.clear(); - for(std::set::iterator - anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) { - if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) { - aNewSetOfCoincides.insert(*anIt); - } - } - aCoincides = aNewSetOfCoincides; - } - - return aCoincides; + return (aLengths[0] < aLengths[1] ? aLengths[0] : aLengths[1]) / 6.0; } std::set findFeaturesToRemove(const FeaturePtr theFeature, @@ -787,7 +594,8 @@ std::set findFeaturesToRemove(const FeaturePtr theFeature, continue; } if(aFeature->getKind() == SketchPlugin_ConstraintLength::ID() - || aFeature->getKind() == SketchPlugin_ConstraintEqual::ID()) { + || aFeature->getKind() == SketchPlugin_ConstraintEqual::ID() + || aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) { aFeaturesToBeRemoved.insert(aFeature); } else { std::list anAttrs =