X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchPlugin%2FSketchPlugin_ConstraintFillet.cpp;h=40c4a83b379a1f6f035552196c7ebf1ffa97877c;hb=e6ab88ed62727ed0f267e133de8965c886d98d78;hp=c311ec712475eb96f82d3c591535c20108a447b5;hpb=ada5c6185cba8cc4352f0207f7540f12e6fda91f;p=modules%2Fshaper.git diff --git a/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp b/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp index c311ec712..40c4a83b3 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp +++ b/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp @@ -13,34 +13,26 @@ #include #include #include -#include #include +#include #include #include -#include #include #include #include #include +#include #include #include #include #include -#include #include -#include -#include - -#include #include -#define _USE_MATH_DEFINES #include -static const std::string PREVIOUS_VALUE("FilletPreviousRadius"); - const double tolerance = 1.e-7; const double paramTolerance = 1.e-4; @@ -64,464 +56,578 @@ static void getPointOnEdge(const FeaturePtr theFeature, static double getProjectionDistance(const FeaturePtr theFeature, const std::shared_ptr thePoint); +/// Get coincide edges for fillet +static std::set getCoincides(const FeaturePtr& theConstraintCoincidence); + SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet() +: myListOfPointsChangedInCode(false), + myRadiusChangedByUser(false), + myRadiusChangedInCode(false), + myRadiusInitialized(false) { } void SketchPlugin_ConstraintFillet::initAttributes() { data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId()); - data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId()); - data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId()); - // This attribute used to store base edges - data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::typeId()); - data()->addAttribute(PREVIOUS_VALUE, ModelAPI_AttributeDouble::typeId()); - // initialize attribute not applicable for user - ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B()); - ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C()); - data()->attribute(PREVIOUS_VALUE)->setInitialized(); - std::dynamic_pointer_cast(data()->attribute(PREVIOUS_VALUE))->setValue(0.0); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId()); } void SketchPlugin_ConstraintFillet::execute() { - static const double aTol = 1.e-7; - - // the viewer update should be blocked in order to avoid the temporaty fillet sub-features visualization - // before they are processed by the solver - //std::shared_ptr aMsg = std::shared_ptr( - // new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED))); - //Events_Loop::loop()->send(aMsg); - std::shared_ptr aData = data(); - ResultConstructionPtr aRC; - // Check the base objects are initialized - double aFilletRadius = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Constraint::VALUE()))->value(); - AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Constraint::ENTITY_A())); - if (!aBaseA->isInitialized() || aBaseA->isObject()) { - setError("Bad vertex selected"); - return; - } - // Obtain fillet point - std::shared_ptr aBasePoint = std::dynamic_pointer_cast(aBaseA->attr()); - if (!aBasePoint) { - setError("Bad vertex selected"); + // Check the base objects are initialized. + AttributeRefAttrListPtr aPointsRefList = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::ENTITY_A())); + if(!aPointsRefList->isInitialized()) { + setError("Error: List of points is not initialized."); return; } - std::shared_ptr aFilletPoint = aBasePoint->pnt(); - // Check the fillet shapes is not initialized yet - AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Constraint::ENTITY_B())); - bool needNewObjects = aRefListOfFillet->size() == 0; + // Get fillet radius. + double aFilletRadius = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::VALUE()))->value(); - AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Constraint::ENTITY_C())); + // Wait all constraints being created, then send update events + static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); + bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent); + if (isUpdateFlushed) + Events_Loop::loop()->setFlushed(anUpdateEvent, false); - // Obtain base features - FeaturePtr anOldFeatureA, anOldFeatureB; - std::list anOldFeatList = aRefListOfBaseLines->list(); - std::list::iterator aFeatIt = anOldFeatList.begin(); - anOldFeatureA = ModelAPI_Feature::feature(*aFeatIt++); - anOldFeatureB = ModelAPI_Feature::feature(*aFeatIt); + for(std::set::iterator aPointsIter = myNewPoints.begin(); + aPointsIter != myNewPoints.end(); + ++aPointsIter) { + AttributePtr aPointAttr = *aPointsIter; + std::shared_ptr aFilletPoint2d = std::dynamic_pointer_cast(aPointAttr); + if(!aFilletPoint2d.get()) { + setError("Error: One of the selected points is empty."); + return; + } + std::shared_ptr aFilletPnt2d = aFilletPoint2d->pnt(); + + // Obtain base lines for fillet. + bool anIsNeedNewObjects = true; + FilletFeatures aFilletFeatures; + std::map::iterator aPrevPointsIter = myPointFeaturesMap.find(aPointAttr); + if(aPrevPointsIter != myPointFeaturesMap.end()) { + anIsNeedNewObjects = false; + aFilletFeatures = aPrevPointsIter->second; + } + FeaturePtr aBaseEdgeA, aBaseEdgeB; + if(!anIsNeedNewObjects) { + aBaseEdgeA = aFilletFeatures.baseEdgesState.front().first; + aBaseEdgeB = aFilletFeatures.baseEdgesState.back().first; + } else { + // Obtain constraint coincidence for the fillet point. + FeaturePtr aConstraintCoincidence; + const std::set& aRefsList = aFilletPoint2d->owner()->data()->refsToMe(); + for(std::set::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) { + std::shared_ptr anAttr = (*anIt); + FeaturePtr aConstrFeature = std::dynamic_pointer_cast(anAttr->owner()); + if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { + AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast( + aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A())); + AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast( + aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B())); + if(anAttrRefA.get() && !anAttrRefA->isObject()) { + AttributePtr anAttrA = anAttrRefA->attr(); + if(aFilletPoint2d == anAttrA) { + aConstraintCoincidence = aConstrFeature; + break; + } + } + if(anAttrRefB.get() && !anAttrRefB->isObject()) { + AttributePtr anAttrB = anAttrRefB->attr(); + if(aFilletPoint2d == anAttrB) { + aConstraintCoincidence = aConstrFeature; + break; + } + } + } + } - if(!anOldFeatureA.get() || !anOldFeatureB.get()) { - setError("One of the edges is empty"); - return; - } + if(!aConstraintCoincidence.get()) { + setError("Error: No coincident edges at one of the selected points."); + return; + } - FeaturePtr aNewFeatureA, aNewFeatureB, aNewArc; - if (needNewObjects) { - // Create list of objects composing a fillet - // copy aFeatureA - aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureA, sketch()); - // copy aFeatureB - aNewFeatureB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureB, sketch()); - // create filleting arc (it will be attached to the list later) - aNewArc = sketch()->addFeature(SketchPlugin_Arc::ID()); - } else { - // Obtain features from the list - std::list aNewFeatList = aRefListOfFillet->list(); - std::list::iterator aFeatIt = aNewFeatList.begin(); - aNewFeatureA = ModelAPI_Feature::feature(*aFeatIt++); - aNewFeatureB = ModelAPI_Feature::feature(*aFeatIt++); - aNewArc = ModelAPI_Feature::feature(*aFeatIt); - } + // Get coincide edges. + std::set aCoincides = getCoincides(aConstraintCoincidence); + if(aCoincides.size() != 2) { + setError("Error: One of the selected points does not have two suitable edges for fillet."); + return; + } - // Calculate arc attributes - static const int aNbFeatures = 2; - FeaturePtr aFeature[aNbFeatures] = {anOldFeatureA, anOldFeatureB}; - FeaturePtr aNewFeature[aNbFeatures] = {aNewFeatureA, aNewFeatureB}; - std::shared_ptr aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point - bool isStart[aNbFeatures]; // indicates which point the features share - std::shared_ptr aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair - to second - std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features - for (int i = 0; i < aNbFeatures; i++) { - std::string aStartAttr, aEndAttr; - if (aNewFeature[i]->getKind() == SketchPlugin_Line::ID()) { - aStartAttr = SketchPlugin_Line::START_ID(); - aEndAttr = SketchPlugin_Line::END_ID(); - } else if (aNewFeature[i]->getKind() == SketchPlugin_Arc::ID()) { - aStartAttr = SketchPlugin_Arc::START_ID(); - aEndAttr = SketchPlugin_Arc::END_ID(); - } else { // wrong argument - aRefListOfFillet->remove(aNewFeatureA); - aRefListOfFillet->remove(aNewFeatureB); - aRefListOfFillet->remove(aNewArc); - aRefListOfBaseLines->clear(); + std::set::iterator aLinesIt = aCoincides.begin(); + aBaseEdgeA = *aLinesIt++; + aBaseEdgeB = *aLinesIt; + + std::pair aBasePairA = std::make_pair(aBaseEdgeA, aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()); + std::pair aBasePairB = std::make_pair(aBaseEdgeB, aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()); + aFilletFeatures.baseEdgesState.push_back(aBasePairA); + aFilletFeatures.baseEdgesState.push_back(aBasePairB); + } + + if(!aBaseEdgeA.get() || !aBaseEdgeB.get()) { + setError("Error: One of the base edges is empty."); return; } - aFeatAttributes[2*i] = aStartAttr; - aStartEndPnt[2*i] = std::dynamic_pointer_cast( - aFeature[i]->attribute(aStartAttr))->pnt(); - aFeatAttributes[2*i+1] = aEndAttr; - aStartEndPnt[2*i+1] = std::dynamic_pointer_cast( - aFeature[i]->attribute(aEndAttr))->pnt(); - } - for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) { - for (int j = 0; j < 2; j++) // loop on start-end of each feature - if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPoint) < 1.e-10) { - isStart[aFeatInd] = (j==0); - break; - } - } - // tangent directions of the features - for (int i = 0; i < aNbFeatures; i++) { - std::shared_ptr aDir; - if (aNewFeature[i]->getKind() == SketchPlugin_Line::ID()) { - aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy()); - if (!isStart[i]) - aDir = aDir->multiplied(-1.0); - } else if (aNewFeature[i]->getKind() == SketchPlugin_Arc::ID()) { - std::shared_ptr aCenterPoint = - std::dynamic_pointer_cast( - aNewFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt(); - aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy(); - aDir = aDir->decreased(aCenterPoint->xy()); - - double x = aDir->x(); - double y = aDir->y(); - aDir->setX(-y); - aDir->setY(x); - if (isStart[i] == std::dynamic_pointer_cast(aFeature[i])->isReversed()) - aDir = aDir->multiplied(-1.0); - } - aTangentDir[i] = std::shared_ptr(new GeomAPI_Dir2d(aDir)); - } - // Wait all constraints being created, then send update events - static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); - bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent); - if (isUpdateFlushed) - Events_Loop::loop()->setFlushed(anUpdateEvent, false); + // Create new edges and arc if needed. + FeaturePtr aResultEdgeA, aResultEdgeB, aResultArc; + if(!anIsNeedNewObjects) { + // Obtain features from the list. + std::list::iterator aResultEdgesIt = aFilletFeatures.resultEdges.begin(); + aResultEdgeA = *aResultEdgesIt++; + aResultEdgeB = *aResultEdgesIt++; + aResultArc = *aResultEdgesIt; + } else { + // Copy edges and create arc. + aResultEdgeA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeA, sketch()); + aResultEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false); + aResultEdgeB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeB, sketch()); + aResultEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false); + aResultArc = sketch()->addFeature(SketchPlugin_Arc::ID()); + + aFilletFeatures.resultEdges.push_back(aResultEdgeA); + aFilletFeatures.resultEdges.push_back(aResultEdgeB); + aFilletFeatures.resultEdges.push_back(aResultArc); + } - // By default, the start point of fillet arc is connected to FeatureA, - // and the end point - to FeatureB. But when the angle between TangentDirA and - // TangentDirB greater 180 degree, the sequaence of features need to be reversed. - double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis - bool isReversed = cosBA > 0.0; - - // Calculate fillet arc parameters - std::shared_ptr aCenter, aTangentPntA, aTangentPntB; - calculateFilletCenter(anOldFeatureA, anOldFeatureB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB); - if(!aCenter.get() || !aTangentPntA.get() || !aTangentPntB.get()) { - setError("Can not create fillet with the specified parameters."); - return; - } - // update features - std::dynamic_pointer_cast( - aNewFeatureA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue( - aTangentPntA->x(), aTangentPntA->y()); - aNewFeatureA->execute(); - std::dynamic_pointer_cast( - aNewFeatureB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue( - aTangentPntB->x(), aTangentPntB->y()); - aNewFeatureB->execute(); - // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated" - // by arc; moreover, it may cause cyclicity in hte mechanism of updater - aNewArc->data()->blockSendAttributeUpdated(true); - std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue( - aCenter->x(), aCenter->y()); - if (isReversed) { - std::shared_ptr aTmp = aTangentPntA; - aTangentPntA = aTangentPntB; - aTangentPntB = aTmp; - } - std::shared_ptr aStartPoint = std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::START_ID())); - std::shared_ptr aEndPoint = std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::END_ID())); - if (aStartPoint->isInitialized() && aEndPoint->isInitialized() && - (aStartPoint->pnt()->xy()->distance(aTangentPntA) > aTol || - aEndPoint->pnt()->xy()->distance(aTangentPntB) > aTol)) - std::dynamic_pointer_cast(aNewArc)->setReversed(false); - aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y()); - aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y()); - aNewArc->data()->blockSendAttributeUpdated(false); - aNewArc->execute(); - - if (needNewObjects) { - // attach new arc to the list - aRefListOfFillet->append(aNewFeatureA->lastResult()); - aRefListOfFillet->append(aNewFeatureB->lastResult()); - aRefListOfFillet->append(aNewArc->lastResult()); - - myProducedFeatures.push_back(aNewFeatureA); - myProducedFeatures.push_back(aNewFeatureB); - myProducedFeatures.push_back(aNewArc); - - // Create list of additional constraints: - // 1. Coincidence of boundary points of features (copied lines/arcs) and fillet arc - // 1.1. coincidence - FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); - AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setAttr(aNewArc->attribute(SketchPlugin_Arc::START_ID())); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - int aFeatInd = isReversed ? 1 : 0; - int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1); - aRefAttr->setAttr(aNewFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd])); - recalculateAttributes(aNewArc, SketchPlugin_Arc::START_ID(), aNewFeature[aFeatInd], aFeatAttributes[anAttrInd]); - aConstraint->execute(); - myProducedFeatures.push_back(aConstraint); - ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); - // 1.2. coincidence - aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setAttr(aNewArc->attribute(SketchPlugin_Arc::END_ID())); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aFeatInd = isReversed ? 0 : 1; - anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1); - aRefAttr->setAttr(aNewFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd])); - recalculateAttributes(aNewArc, SketchPlugin_Arc::END_ID(), aNewFeature[aFeatInd], aFeatAttributes[anAttrInd]); - aConstraint->execute(); - myProducedFeatures.push_back(aConstraint); - ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); - // 2. Fillet arc radius - //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID()); - //aRefAttr = std::dynamic_pointer_cast( - // aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - //aRefAttr->setObject(aNewArc->lastResult()); - //std::dynamic_pointer_cast( - // aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius); - //std::dynamic_pointer_cast( - // aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue( - // isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]); - //aConstraint->execute(); - //myProducedFeatures.push_back(aConstraint); - //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); - // 3. Tangency of fillet arc and features + // Calculate arc attributes + static const int aNbFeatures = 2; + FeaturePtr aBaseFeatures[aNbFeatures] = {aBaseEdgeA, aBaseEdgeB}; + FeaturePtr aResultFeatures[aNbFeatures] = {aResultEdgeA, aResultEdgeB}; + std::shared_ptr aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point + bool isStart[aNbFeatures]; // indicates which point the features share + std::shared_ptr aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair - to second + std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features for (int i = 0; i < aNbFeatures; i++) { - aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID()); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setObject(aNewArc->lastResult()); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - bool isArc = aNewFeature[i]->getKind() == SketchPlugin_Arc::ID(); - aRefAttr->setObject(isArc ? aNewFeature[i]->lastResult() : aNewFeature[i]->firstResult()); - aConstraint->execute(); - myProducedFeatures.push_back(aConstraint); - ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + std::string aStartAttr, aEndAttr; + if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) { + aStartAttr = SketchPlugin_Line::START_ID(); + aEndAttr = SketchPlugin_Line::END_ID(); + } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) { + aStartAttr = SketchPlugin_Arc::START_ID(); + aEndAttr = SketchPlugin_Arc::END_ID(); + } else { // wrong argument + setError("Error: One of the points has wrong coincide feature"); + return; + } + aFeatAttributes[2*i] = aStartAttr; + aStartEndPnt[2*i] = std::dynamic_pointer_cast( + aBaseFeatures[i]->attribute(aStartAttr))->pnt(); + aFeatAttributes[2*i+1] = aEndAttr; + aStartEndPnt[2*i+1] = std::dynamic_pointer_cast( + aBaseFeatures[i]->attribute(aEndAttr))->pnt(); + } + for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) { + for (int j = 0; j < 2; j++) // loop on start-end of each feature + if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPnt2d) < 1.e-10) { + isStart[aFeatInd] = (j==0); + break; + } } - // 4. Coincidence of free boundaries of base and copied features + // tangent directions of the features for (int i = 0; i < aNbFeatures; i++) { - anAttrInd = 2*i + (isStart[i] ? 1 : 0); - aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); - aRefAttr = std::dynamic_pointer_cast( + std::shared_ptr aDir; + if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) { + aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy()); + if (!isStart[i]) + aDir = aDir->multiplied(-1.0); + } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) { + std::shared_ptr aCenterPoint = std::dynamic_pointer_cast( + aResultFeatures[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt(); + aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy(); + aDir = aDir->decreased(aCenterPoint->xy()); + + double x = aDir->x(); + double y = aDir->y(); + aDir->setX(-y); + aDir->setY(x); + if (isStart[i] == std::dynamic_pointer_cast(aBaseFeatures[i])->isReversed()) + aDir = aDir->multiplied(-1.0); + } + aTangentDir[i] = std::shared_ptr(new GeomAPI_Dir2d(aDir)); + } + + // By default, the start point of fillet arc is connected to FeatureA, + // and the end point - to FeatureB. But when the angle between TangentDirA and + // TangentDirB greater 180 degree, the sequaence of features need to be reversed. + double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis + bool isReversed = cosBA > 0.0; + + // Calculate fillet arc parameters + std::shared_ptr aCenter, aTangentPntA, aTangentPntB; + calculateFilletCenter(aBaseEdgeA, aBaseEdgeB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB); + if(!aCenter.get() || !aTangentPntA.get() || !aTangentPntB.get()) { + setError("Can not create fillet with the specified parameters."); + return; + } + // update features + std::dynamic_pointer_cast( + aResultEdgeA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(aTangentPntA->x(), aTangentPntA->y()); + aResultEdgeA->execute(); + std::dynamic_pointer_cast( + aResultEdgeB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue(aTangentPntB->x(), aTangentPntB->y()); + aResultEdgeB->execute(); + // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated" + // by arc; moreover, it may cause cyclicity in hte mechanism of updater + aResultArc->data()->blockSendAttributeUpdated(true); + std::dynamic_pointer_cast( + aResultArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCenter->x(), aCenter->y()); + if(isReversed) { + std::shared_ptr aTmp = aTangentPntA; + aTangentPntA = aTangentPntB; + aTangentPntB = aTmp; + } + std::shared_ptr aStartPoint = std::dynamic_pointer_cast( + aResultArc->attribute(SketchPlugin_Arc::START_ID())); + std::shared_ptr aEndPoint = std::dynamic_pointer_cast( + aResultArc->attribute(SketchPlugin_Arc::END_ID())); + if(aStartPoint->isInitialized() && aEndPoint->isInitialized() && + (aStartPoint->pnt()->xy()->distance(aTangentPntA) > tolerance || + aEndPoint->pnt()->xy()->distance(aTangentPntB) > tolerance)) { + std::dynamic_pointer_cast(aResultArc)->setReversed(false); + } + aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y()); + aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y()); + aResultArc->data()->blockSendAttributeUpdated(false); + aResultArc->execute(); + + if(anIsNeedNewObjects) { + // Create list of additional constraints: + // 1. Coincidence of boundary points of features (copied lines/arcs) and fillet arc + // 1.1. coincidence + FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setAttr(aFeature[i]->attribute(aFeatAttributes[anAttrInd])); + aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::START_ID())); aRefAttr = std::dynamic_pointer_cast( aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttr->setAttr(aNewFeature[i]->attribute(aFeatAttributes[anAttrInd])); - myProducedFeatures.push_back(aConstraint); - } - // 5. Tangent points should be placed on the base features - for (int i = 0; i < aNbFeatures; i++) { - anAttrInd = 2*i + (isStart[i] ? 0 : 1); + int aFeatInd = isReversed ? 1 : 0; + int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1); + aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd])); + recalculateAttributes(aResultArc, SketchPlugin_Arc::START_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]); + aConstraint->execute(); + aFilletFeatures.resultConstraints.push_back(aConstraint); + ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + // 1.2. coincidence aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); aRefAttr = std::dynamic_pointer_cast( aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setAttr(aNewFeature[i]->attribute(aFeatAttributes[anAttrInd])); + aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::END_ID())); aRefAttr = std::dynamic_pointer_cast( aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttr->setObject(aFeature[i]->lastResult()); - myProducedFeatures.push_back(aConstraint); - } - // make base features auxiliary - anOldFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); - anOldFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); - myBaseObjects.clear(); - myBaseObjects.push_back(anOldFeatureA); - myBaseObjects.push_back(anOldFeatureB); - // exchange the naming IDs of newly created and old line that become auxiliary - sketch()->exchangeIDs(anOldFeatureA, aNewFeatureA); - sketch()->exchangeIDs(anOldFeatureB, aNewFeatureB); - } else { - // Update radius value - int aNbSubs = sketch()->numberOfSubs(); - FeaturePtr aSubFeature; - for (int aSub = 0; aSub < aNbSubs; aSub++) { - aSubFeature = sketch()->subFeature(aSub); - if (aSubFeature->getKind() != SketchPlugin_ConstraintRadius::ID()) - continue; - AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( - aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A())); - if (!aRefAttr || !aRefAttr->isObject()) - continue; - FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object()); - if (aFeature == aNewArc) { - AttributeDoublePtr aRadius = std::dynamic_pointer_cast( - aSubFeature->attribute(SketchPlugin_Constraint::VALUE())); - aRadius->setValue(aFilletRadius); - break; + aFeatInd = isReversed ? 0 : 1; + anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1); + aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd])); + recalculateAttributes(aResultArc, SketchPlugin_Arc::END_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]); + aConstraint->execute(); + aFilletFeatures.resultConstraints.push_back(aConstraint); + ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + // 2. Fillet arc radius + //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID()); + //aRefAttr = std::dynamic_pointer_cast( + // aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); + //aRefAttr->setObject(aNewArc->lastResult()); + //std::dynamic_pointer_cast( + // aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius); + //std::dynamic_pointer_cast( + // aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue( + // isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]); + //aConstraint->execute(); + //myProducedFeatures.push_back(aConstraint); + //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + // 3. Tangency of fillet arc and features + for (int i = 0; i < aNbFeatures; i++) { + aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID()); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); + aRefAttr->setObject(aResultArc->lastResult()); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); + bool isArc = aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID(); + aRefAttr->setObject(isArc ? aResultFeatures[i]->lastResult() : aResultFeatures[i]->firstResult()); + aConstraint->execute(); + aFilletFeatures.resultConstraints.push_back(aConstraint); + ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + } + // 4. Coincidence of free boundaries of base and copied features + for (int i = 0; i < aNbFeatures; i++) { + anAttrInd = 2*i + (isStart[i] ? 1 : 0); + aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); + aRefAttr->setAttr(aBaseFeatures[i]->attribute(aFeatAttributes[anAttrInd])); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); + aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd])); + aFilletFeatures.resultConstraints.push_back(aConstraint); + } + // 4.1. Additional tangency constraints when the fillet is based on arcs. + // It is used to verify the created arc will be placed on a source. + for (int i = 0; i < aNbFeatures; ++i) { + if (aResultFeatures[i]->getKind() != SketchPlugin_Arc::ID()) + continue; + aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID()); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); + aRefAttr->setObject(aBaseFeatures[i]->lastResult()); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); + aRefAttr->setObject(aResultFeatures[i]->lastResult()); + aConstraint->execute(); + aFilletFeatures.resultConstraints.push_back(aConstraint); + ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + } + // 5. Tangent points should be placed on the base features + for (int i = 0; i < aNbFeatures; i++) { + anAttrInd = 2*i + (isStart[i] ? 0 : 1); + aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID()); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); + aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd])); + aRefAttr = std::dynamic_pointer_cast( + aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); + aRefAttr->setObject(aBaseFeatures[i]->lastResult()); + aFilletFeatures.resultConstraints.push_back(aConstraint); + } + // make base features auxiliary + aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); + aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); + + // exchange the naming IDs of newly created and old line that become auxiliary + sketch()->exchangeIDs(aBaseEdgeA, aResultEdgeA); + sketch()->exchangeIDs(aBaseEdgeB, aResultEdgeB); + + // store point and features in the map. + myPointFeaturesMap[aPointAttr] = aFilletFeatures; + } else { + // Update radius value + int aNbSubs = sketch()->numberOfSubs(); + FeaturePtr aSubFeature; + for (int aSub = 0; aSub < aNbSubs; aSub++) { + aSubFeature = sketch()->subFeature(aSub); + if (aSubFeature->getKind() != SketchPlugin_ConstraintRadius::ID()) + continue; + AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast( + aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A())); + if (!aRefAttr || !aRefAttr->isObject()) + continue; + FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object()); + if (aFeature == aResultArc) { + AttributeDoublePtr aRadius = std::dynamic_pointer_cast( + aSubFeature->attribute(SketchPlugin_Constraint::VALUE())); + aRadius->setValue(aFilletRadius); + break; + } } } } - // send events to update the sub-features by the solver - if (isUpdateFlushed) + // Send events to update the sub-features by the solver. + if(isUpdateFlushed) { Events_Loop::loop()->setFlushed(anUpdateEvent, true); - - // the viewer update should be unblocked in order after the fillet features - // are processed by the solver - //aMsg = std::shared_ptr( - // new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED))); - //Events_Loop::loop()->send(aMsg); + } } void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID) { - if (theID == SketchPlugin_Constraint::ENTITY_A()) { - // clear the list of fillet entities - AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast( - data()->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefListOfFillet->clear(); - - // clear the list of base features - AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast( - data()->attribute(SketchPlugin_Constraint::ENTITY_C())); - aRefListOfBaseLines->clear(); - - // remove all produced objects and constraints - DocumentPtr aDoc = sketch()->document(); - std::list::iterator aCIt = myProducedFeatures.begin(); - for (; aCIt != myProducedFeatures.end(); ++aCIt) - aDoc->removeFeature(*aCIt); - myProducedFeatures.clear(); - - // clear auxiliary flag on initial objects - for (aCIt = myBaseObjects.begin(); aCIt != myBaseObjects.end(); ++aCIt) - (*aCIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false); - myBaseObjects.clear(); - - // Obtain fillet point - AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast( - data()->attribute(SketchPlugin_Constraint::ENTITY_A())); - if(!aBaseA->isInitialized() || aBaseA->isObject()) { + if(theID == SketchPlugin_Constraint::ENTITY_A()) { + if(myListOfPointsChangedInCode) { return; } - AttributePtr anAttrBaseA = aBaseA->attr(); - std::shared_ptr aBasePoint = std::dynamic_pointer_cast(anAttrBaseA); - if (!aBasePoint) { + + // Clear results. + clearResults(); + + // Clear list of new points. + myNewPoints.clear(); + + // Get list of points for fillets and current radius. + AttributeRefAttrListPtr aRefListOfFilletPoints = std::dynamic_pointer_cast( + data()->attribute(SketchPlugin_Constraint::ENTITY_A())); + AttributeDoublePtr aRadiusAttribute = real(VALUE()); + int aListSize = aRefListOfFilletPoints->size(); + if(aListSize == 0 && !myRadiusChangedByUser) { + // If list is empty reset radius to zero (if it was not changed by user). + myRadiusChangedInCode = true; + aRadiusAttribute->setValue(0); + myRadiusChangedInCode = false; return; } - std::shared_ptr aFilletPoint = aBasePoint->pnt(); - - // Obtain conicident edges - const std::set& aRefsList = anAttrBaseA->owner()->data()->refsToMe(); - std::set::const_iterator aIt; - FeaturePtr aCoincident; - for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) { - std::shared_ptr aAttr = (*aIt); - FeaturePtr aConstrFeature = std::dynamic_pointer_cast(aAttr->owner()); - if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { - AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast( - aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A())); - AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast( - aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B())); - if(anAttrRefA.get() && !anAttrRefA->isObject()) { - AttributePtr anAttrA = anAttrRefA->attr(); - if(anAttrBaseA == anAttrA) { - aCoincident = aConstrFeature; - break; + + // Iterate over points to get base lines an calculate radius for fillets. + double aMinimumRadius = 0; + std::list> aSelectedPointsList = aRefListOfFilletPoints->list(); + std::list>::iterator anIter = aSelectedPointsList.begin(); + std::set aPointsToSkeep; + for(int anIndex = 0; anIndex < aListSize; anIndex++, anIter++) { + AttributePtr aFilletPointAttr = (*anIter).second; + std::shared_ptr aFilletPoint2D = + std::dynamic_pointer_cast(aFilletPointAttr); + if(!aFilletPoint2D.get()) { + myNewPoints.clear(); + setError("Error: One of the selected points is invalid."); + return; + } + + // If point or coincident point is already in list remove it from attribute. + if(aPointsToSkeep.find(aFilletPointAttr) != aPointsToSkeep.end()) { + myListOfPointsChangedInCode = true; + aRefListOfFilletPoints->remove(aFilletPointAttr); + myListOfPointsChangedInCode = false; + continue; + } + + // Obtain constraint coincidence for the fillet point. + FeaturePtr aConstraintCoincidence; + const std::set& aRefsList = aFilletPointAttr->owner()->data()->refsToMe(); + for(std::set::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) { + std::shared_ptr anAttr = (*anIt); + FeaturePtr aConstrFeature = std::dynamic_pointer_cast(anAttr->owner()); + if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) { + AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast( + aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A())); + AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast( + aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B())); + if(anAttrRefA.get()) { + AttributePtr anAttrA = anAttrRefA->attr(); + if(aFilletPointAttr == anAttrA) { + aConstraintCoincidence = aConstrFeature; + break; + } } - } - if(anAttrRefA.get() && !anAttrRefB->isObject()) { - AttributePtr anAttrB = anAttrRefB->attr(); - if(anAttrBaseA == anAttrB) { - aCoincident = aConstrFeature; - break; + if(anAttrRefB.get()) { + AttributePtr anAttrB = anAttrRefB->attr(); + if(aFilletPointAttr == anAttrB) { + aConstraintCoincidence = aConstrFeature; + break; + } } } } - } - if(!aCoincident.get()) { - setError("No coincident edges at selected vertex"); - return; - } + if(!aConstraintCoincidence.get()) { + myNewPoints.clear(); + setError("Error: No coincident edges at one of the selected points."); + return; + } - std::set aCoinsides; - SketchPlugin_Tools::findCoincidences(aCoincident, - SketchPlugin_ConstraintCoincidence::ENTITY_A(), - aCoinsides); - SketchPlugin_Tools::findCoincidences(aCoincident, - SketchPlugin_ConstraintCoincidence::ENTITY_B(), - aCoinsides); + // Get coincides from constraint. + std::set aCoincides; + + + SketchPlugin_Tools::findCoincidences(aConstraintCoincidence, + SketchPlugin_ConstraintCoincidence::ENTITY_A(), + aCoincides); + SketchPlugin_Tools::findCoincidences(aConstraintCoincidence, + SketchPlugin_ConstraintCoincidence::ENTITY_B(), + aCoincides); + + // Remove points from set of coincides. Also get all attributes which is equal to this point to exclude it. + std::shared_ptr aFilletPnt2d = aFilletPoint2D->pnt(); + std::set aNewSetOfCoincides; + for(std::set::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) { + std::string aFeatureKind = (*anIt)->getKind(); + if(aFeatureKind == SketchPlugin_Point::ID()) { + AttributePtr anAttr = (*anIt)->attribute(SketchPlugin_Point::COORD_ID()); + std::shared_ptr aPoint2D = + std::dynamic_pointer_cast(anAttr); + if(aPoint2D.get() && aFilletPnt2d->isEqual(aPoint2D->pnt())) { + aPointsToSkeep.insert(anAttr); + } + } else if(aFeatureKind == SketchPlugin_Line::ID()) { + AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Line::START_ID()); + std::shared_ptr aPointStart2D = + std::dynamic_pointer_cast(anAttrStart); + if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) { + aPointsToSkeep.insert(anAttrStart); + } + AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Line::END_ID()); + std::shared_ptr aPointEnd2D = + std::dynamic_pointer_cast(anAttrEnd); + if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) { + aPointsToSkeep.insert(anAttrEnd); + } + aNewSetOfCoincides.insert(*anIt); + } else if(aFeatureKind == SketchPlugin_Arc::ID() ) { + AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Arc::START_ID()); + std::shared_ptr aPointStart2D = + std::dynamic_pointer_cast(anAttrStart); + if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) { + aPointsToSkeep.insert(anAttrStart); + } + AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Arc::END_ID()); + std::shared_ptr aPointEnd2D = + std::dynamic_pointer_cast(anAttrEnd); + if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) { + aPointsToSkeep.insert(anAttrEnd); + } + aNewSetOfCoincides.insert(*anIt); + } + } + 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; + } - // Remove points - std::set aNewLines; - for(std::set::iterator anIt = aCoinsides.begin(); anIt != aCoinsides.end(); ++anIt) { - if((*anIt)->getKind() != SketchPlugin_Point::ID()) { - aNewLines.insert(*anIt); + if(aCoincides.size() != 2) { + myNewPoints.clear(); + setError("Error: One of the selected points does not have two suitable edges for fillet."); + return; } - } - aCoinsides = aNewLines; - // Remove auxilary lines - if(aCoinsides.size() > 2) { - aNewLines.clear(); - for(std::set::iterator anIt = aCoinsides.begin(); anIt != aCoinsides.end(); ++anIt) { - if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) { - aNewLines.insert(*anIt); - } + // Store base point for fillet. + aPointsToSkeep.insert(aFilletPointAttr); + myNewPoints.insert(aFilletPointAttr); + + // Get base lines for fillet. + FeaturePtr anOldFeatureA, anOldFeatureB; + std::set::iterator aLinesIt = aCoincides.begin(); + anOldFeatureA = *aLinesIt++; + anOldFeatureB = *aLinesIt; + + // Getting radius value if it was not changed by user. + if(!myRadiusChangedByUser) { + // Getting points located at 1/3 of edge length from fillet point. + std::shared_ptr aFilletPnt2d = aFilletPoint2D->pnt(); + std::shared_ptr aPntA, aPntB; + getPointOnEdge(anOldFeatureA, aFilletPnt2d, aPntA); + getPointOnEdge(anOldFeatureB, aFilletPnt2d, aPntB); + + /// Getting distances. + double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA); + double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB); + double aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0; + aMinimumRadius = aMinimumRadius == 0 ? aRadius : aRadius < aMinimumRadius ? aRadius : aMinimumRadius; } - aCoinsides = aNewLines; } - if(aCoinsides.size() != 2) { - setError("At selected vertex should be two coincident lines"); - return; + // Set new default radius if it was not changed by user. + if(!myRadiusChangedByUser) { + myRadiusChangedInCode = true; + aRadiusAttribute->setValue(aMinimumRadius); + myRadiusChangedInCode = false; } - // Store base lines - FeaturePtr anOldFeatureA, anOldFeatureB; - std::set::iterator aLinesIt = aCoinsides.begin(); - anOldFeatureA = *aLinesIt++; - anOldFeatureB = *aLinesIt; - aRefListOfBaseLines->append(anOldFeatureA); - aRefListOfBaseLines->append(anOldFeatureB); - - // Getting points located at 1/3 of edge length from fillet point - std::shared_ptr aPntA, aPntB; - getPointOnEdge(anOldFeatureA, aFilletPoint, aPntA); - getPointOnEdge(anOldFeatureB, aFilletPoint, aPntB); - - /// Getting distances - double aRadius = 1; - double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA); - double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB); - aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0; - - std::dynamic_pointer_cast(data()->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aRadius); + } else if(theID == SketchPlugin_Constraint::VALUE()) { + if(myRadiusInitialized && !myRadiusChangedInCode) { + myRadiusChangedByUser = true; + } + if(!myRadiusInitialized) { + myRadiusInitialized = true; + } } } @@ -540,6 +646,46 @@ bool SketchPlugin_ConstraintFillet::isMacro() const return true; } +void SketchPlugin_ConstraintFillet::clearResults() +{ + // Clear auxiliary flag on initial objects. + for(std::map::iterator aPointsIter = myPointFeaturesMap.begin(); + aPointsIter != myPointFeaturesMap.end();) { + const FilletFeatures& aFilletFeatures = aPointsIter->second; + std::list>::const_iterator aFeatureIt; + for(aFeatureIt = aFilletFeatures.baseEdgesState.cbegin(); + aFeatureIt != aFilletFeatures.baseEdgesState.cend(); + ++aFeatureIt) { + aFeatureIt->first->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(aFeatureIt->second); + } + ++aPointsIter; + } + + // And remove all produced features. + DocumentPtr aDoc = sketch()->document(); + for(std::map::iterator aPointsIter = myPointFeaturesMap.begin(); + aPointsIter != myPointFeaturesMap.end();) { + // Remove all produced constraints. + const FilletFeatures& aFilletFeatures = aPointsIter->second; + std::list::const_iterator aFeatureIt; + for(aFeatureIt = aFilletFeatures.resultConstraints.cbegin(); + aFeatureIt != aFilletFeatures.resultConstraints.cend(); + ++aFeatureIt) { + aDoc->removeFeature(*aFeatureIt); + } + + // Remove all result edges. + for(aFeatureIt = aFilletFeatures.resultEdges.cbegin(); + aFeatureIt != aFilletFeatures.resultEdges.cend(); + ++aFeatureIt) { + aDoc->removeFeature(*aFeatureIt); + } + + // Remove point from map. + myPointFeaturesMap.erase(aPointsIter++); + } +}; + // ========= Auxiliary functions ================= void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute, @@ -881,3 +1027,38 @@ double getProjectionDistance(const FeaturePtr theFeature, } return -1; } + +std::set getCoincides(const FeaturePtr& theConstraintCoincidence) +{ + std::set aCoincides; + + 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) { + if((*anIt)->getKind() == SketchPlugin_Line::ID() || + (*anIt)->getKind() == SketchPlugin_Arc::ID() ) { + aNewSetOfCoincides.insert(*anIt); + } + } + 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; +}