From fe2864e49907accdccfc45fd8fe60176b7b31f39 Mon Sep 17 00:00:00 2001 From: dbv Date: Wed, 20 Jan 2016 11:27:05 +0300 Subject: [PATCH] Fillet multiselection --- src/Model/Model_AttributeRefAttrList.cpp | 3 +- .../SketchPlugin_ConstraintFillet.cpp | 962 ++++++++++-------- .../SketchPlugin_ConstraintFillet.h | 5 +- src/SketchPlugin/SketchPlugin_Validators.cpp | 251 +++-- src/SketchPlugin/SketchPlugin_Validators.h | 2 +- src/SketchPlugin/Test/TestFillet.py | 97 +- src/SketchPlugin/plugin-Sketch.xml | 12 +- 7 files changed, 738 insertions(+), 594 deletions(-) diff --git a/src/Model/Model_AttributeRefAttrList.cpp b/src/Model/Model_AttributeRefAttrList.cpp index e08420a01..94b301921 100755 --- a/src/Model/Model_AttributeRefAttrList.cpp +++ b/src/Model/Model_AttributeRefAttrList.cpp @@ -95,7 +95,7 @@ void Model_AttributeRefAttrList::remove(AttributePtr theAttr) (aTheObjLab.IsNull() && aDoc->objects()->object(aRefIter.Value()) != NULL)) { myRef->Append(aRefIter.Value()); myIDs->Append(anIDIter.Value()); - } else if (aTheObjLab.IsNull() && aDoc->objects()->object(aRefIter.Value()) != NULL) { + } else { aOneisDeleted = true; } } @@ -293,7 +293,6 @@ void Model_AttributeRefAttrList::remove(const std::set& theIndices) aOneisDeleted = true; ObjectPtr anObj = aDoc->objects()->object(aRefIter.Value()); if (anObj.get()) { - myRef->Remove(aRefIter.Value()); REMOVE_BACK_REF(anObj); } } diff --git a/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp b/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp index 0b4514752..afbd1407a 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,480 +56,591 @@ 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(true) { } 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 + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId()); + AttributePtr aResultEdgesRefList = data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId()); // Used to store result edges. + AttributePtr aBasePointsRefAttrList = data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefAttrList::typeId()); // Used to store base points. + // 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); + aResultEdgesRefList->setIsArgument(false); + aBasePointsRefAttrList->setIsArgument(false); } 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; - AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Constraint::ENTITY_C())); + // Get fillet radius. + double aFilletRadius = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::VALUE()))->value(); - // 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); + // Check the fillet result edges is not initialized yet. + AttributeRefListPtr aResultEdgesRefList = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::ENTITY_B())); + bool anIsNeedNewObjects = aResultEdgesRefList->size() == 0; - if(!anOldFeatureA.get() || !anOldFeatureB.get()) { - setError("One of the edges is empty"); - 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); - } + // 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); - // 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(); + for(int anIndex = 0; anIndex < aPointsRefList->size(); ++anIndex) { + std::shared_ptr aFilletPoint2d = + std::dynamic_pointer_cast(aPointsRefList->attribute(anIndex)); + if(!aFilletPoint2d.get()) { + setError("Error: One of the selected points 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; + std::shared_ptr aFilletPnt2d = aFilletPoint2d->pnt(); + + // Obtain base lines for fillet. + FeaturePtr aBaseEdgeA, aBaseEdgeB; + if(myBaseEdges.size() > (unsigned int)(anIndex * 2)) { + std::list::iterator anIter = myBaseEdges.begin(); + std::advance(anIter, anIndex * 2); + aBaseEdgeA = *anIter++; + aBaseEdgeB = *anIter; + } 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; + } + } + } } - } - // 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); + + if(!aConstraintCoincidence.get()) { + setError("Error: No coincident edges at one of the selected points."); + return; + } + + // 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; + } + + std::set::iterator aLinesIt = aCoincides.begin(); + aBaseEdgeA = *aLinesIt++; + aBaseEdgeB = *aLinesIt; } - 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); + if(!aBaseEdgeA.get() || !aBaseEdgeB.get()) { + setError("Error: One of the base edges is empty."); + return; + } - // 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 + // Create new edges and arc if needed. + FeaturePtr aResultEdgeA, aResultEdgeB, aResultArc; + if(anIsNeedNewObjects) { + // Copy edges and create arc. + aResultEdgeA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeA, sketch()); + aResultEdgeB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeB, sketch()); + aResultArc = sketch()->addFeature(SketchPlugin_Arc::ID()); + } else { + // Obtain features from the list. + aResultEdgeA = ModelAPI_Feature::feature(aResultEdgesRefList->object(anIndex * 3)); + aResultEdgeB = ModelAPI_Feature::feature(aResultEdgesRefList->object(anIndex * 3 + 1)); + aResultArc = ModelAPI_Feature::feature(aResultEdgesRefList->object(anIndex * 3 + 2)); + } + + // 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 + aResultEdgesRefList->clear(); + 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( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A())); - aRefAttr->setAttr(aFeature[i]->attribute(aFeatAttributes[anAttrInd])); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttr->setAttr(aNewFeature[i]->attribute(aFeatAttributes[anAttrInd])); - myProducedFeatures.push_back(aConstraint); + 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)); } - // 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 (aNewFeature[i]->getKind() != SketchPlugin_Arc::ID()) - continue; - aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID()); - aRefAttr = std::dynamic_pointer_cast( + + // 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) { + // attach new arc to the list + aResultEdgesRefList->append(aResultEdgeA->lastResult()); + aResultEdgesRefList->append(aResultEdgeB->lastResult()); + aResultEdgesRefList->append(aResultArc->lastResult()); + + myProducedFeatures.push_back(aResultEdgeA); + myProducedFeatures.push_back(aResultEdgeB); + myProducedFeatures.push_back(aResultArc); + + // 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->setObject(aFeature[i]->lastResult()); + aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::START_ID())); aRefAttr = std::dynamic_pointer_cast( aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - aRefAttr->setObject(aNewFeature[i]->lastResult()); + 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(); myProducedFeatures.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); + // 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()); + 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(); 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; + 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(); + myProducedFeatures.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])); + myProducedFeatures.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(); + myProducedFeatures.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()); + myProducedFeatures.push_back(aConstraint); + } + // make base features auxiliary + aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); + aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); + + myBaseEdges.push_back(aBaseEdgeA); + myBaseEdges.push_back(aBaseEdgeB); + // exchange the naming IDs of newly created and old line that become auxiliary + sketch()->exchangeIDs(aBaseEdgeA, aResultEdgeA); + sketch()->exchangeIDs(aBaseEdgeB, aResultEdgeB); + } 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( + if(theID == SketchPlugin_Constraint::ENTITY_A()) { + if(myListOfPointsChangedInCode) { + return; + } + + // Clear the list of fillet entities. + AttributeRefListPtr aResultEdgesRefList = std::dynamic_pointer_cast( + data()->attribute(SketchPlugin_Constraint::ENTITY_B())); + aResultEdgesRefList->clear(); + + // Clear the list of base points. + AttributeRefAttrListPtr aBasePointsRefAttrList = std::dynamic_pointer_cast( data()->attribute(SketchPlugin_Constraint::ENTITY_C())); - aRefListOfBaseLines->clear(); + aBasePointsRefAttrList->clear(); + + // Clear auxiliary flag on initial objects. + std::list::const_iterator aFeatureIt; + for(aFeatureIt = myBaseEdges.cbegin(); aFeatureIt != myBaseEdges.cend(); ++aFeatureIt) { + (*aFeatureIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false); + } + myBaseEdges.clear(); - // remove all produced objects and constraints + // Remove all produced objects and constraints. DocumentPtr aDoc = sketch()->document(); - std::list::iterator aCIt = myProducedFeatures.begin(); - for (; aCIt != myProducedFeatures.end(); ++aCIt) - aDoc->removeFeature(*aCIt); + for(aFeatureIt = myProducedFeatures.cbegin(); aFeatureIt != myProducedFeatures.cend(); ++aFeatureIt) { + aDoc->removeFeature(*aFeatureIt); + } 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()) { - return; - } - AttributePtr anAttrBaseA = aBaseA->attr(); - std::shared_ptr aBasePoint = std::dynamic_pointer_cast(anAttrBaseA); - if (!aBasePoint) { + // Get list of points for fillets and current radius. + AttributeRefAttrListPtr aRefListOfFilletPoints = std::dynamic_pointer_cast( + data()->attribute(SketchPlugin_Constraint::ENTITY_A())); + AttributeDoublePtr aRadiusAttribute = real(SketchPlugin_Constraint::VALUE()); + int aListSize = aRefListOfFilletPoints->size(); + if(aListSize == 0 && !myRadiusChangedByUser) { + // If list is empty just 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 aBasePoints; + for(int anIndex = 0; anIndex < aListSize; anIndex++, anIter++) { + AttributePtr aFilletPointAttr = (*anIter).second; + std::shared_ptr aFilletPoint2D = + std::dynamic_pointer_cast(aFilletPointAttr); + if(!aFilletPoint2D.get()) { + setError("Error: One of the selected points is invalid."); + return; + } + + // If point or coincident point is already in list remove it from attribute. + if(aBasePoints.find(aFilletPointAttr) != aBasePoints.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()) { + 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); - - // 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); + // 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())) { + aBasePoints.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())) { + aBasePoints.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())) { + aBasePoints.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())) { + aBasePoints.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())) { + aBasePoints.insert(anAttrEnd); + } + aNewSetOfCoincides.insert(*anIt); + } } - } - 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); + 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; + } + + if(aCoincides.size() != 2) { + setError("Error: One of the selected points does not have two suitable edges for fillet."); + return; + } + + // Store base point for fillet. + aBasePoints.insert(aFilletPointAttr); + aBasePointsRefAttrList->append(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(!myRadiusChangedInCode) { + myRadiusChangedByUser = true; + } } } @@ -897,3 +1000,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; +} diff --git a/src/SketchPlugin/SketchPlugin_ConstraintFillet.h b/src/SketchPlugin/SketchPlugin_ConstraintFillet.h index 909473bac..5865c0746 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintFillet.h +++ b/src/SketchPlugin/SketchPlugin_ConstraintFillet.h @@ -60,7 +60,10 @@ class SketchPlugin_ConstraintFillet : public SketchPlugin_ConstraintBase private: std::list myProducedFeatures; ///< list of constraints provided by the fillet - std::list myBaseObjects; ///< list of objects the fillet is based + std::list myBaseEdges; ///< list of objects the fillet is based + bool myListOfPointsChangedInCode; ///< flag to track that list of points changed in code + bool myRadiusChangedByUser; ///< flag to track that radius changed by user + bool myRadiusChangedInCode; ///< flag to track that radius changed in code }; #endif diff --git a/src/SketchPlugin/SketchPlugin_Validators.cpp b/src/SketchPlugin/SketchPlugin_Validators.cpp index 612b083e7..46bbf6a66 100755 --- a/src/SketchPlugin/SketchPlugin_Validators.cpp +++ b/src/SketchPlugin/SketchPlugin_Validators.cpp @@ -22,11 +22,15 @@ #include #include #include + +#include #include #include #include #include +#include +#include #include const double tolerance = 1.e-7; @@ -201,7 +205,7 @@ bool SketchPlugin_TangentAttrValidator::isValid(const AttributePtr& theAttribute return true; } -bool SketchPlugin_NotFixedValidator::isValid(const AttributePtr& theAttribute, +bool SketchPlugin_NotFixedValidator::isValid(const AttributePtr& theAttribute, const std::list& theArguments, std::string& theError) const { @@ -457,160 +461,143 @@ bool SketchPlugin_FilletVertexValidator::isValid(const AttributePtr& theAttribut const std::list& theArguments, std::string& theError) const { - if(!theAttribute.get()) { + if(!theAttribute.get() || !theAttribute->isInitialized()) { + theError = "Error: List of points is not initialized."; return false; } - AttributeRefAttrPtr aBase = std::dynamic_pointer_cast(theAttribute); - AttributePtr anAttrBase = aBase->attr(); - if(aBase->isObject()) { + FeaturePtr aFilletFeature = std::dynamic_pointer_cast(theAttribute->owner()); + AttributeRefAttrListPtr aPointsRefList = std::dynamic_pointer_cast(theAttribute); + if(aPointsRefList->size() == 0) { + theError = "Error: List of points is empty."; return false; } - // If we alredy have some result then: - // - if it is the same point all ok - // - if it is point on the fillet arc then it is not valid - FeaturePtr aFeature = std::dynamic_pointer_cast(theAttribute->owner()); - AttributePtr aBaseLinesAttribute = aFeature->attribute(SketchPlugin_Constraint::ENTITY_C()); - AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast(aBaseLinesAttribute); - std::list anOldFeatList = aRefListOfBaseLines->list(); - if(!aRefListOfBaseLines->list().empty()) { - FeaturePtr anOldFeatureA, anOldFeatureB; - std::list::iterator aFeatIt = anOldFeatList.begin(); - anOldFeatureA = ModelAPI_Feature::feature(*aFeatIt++); - anOldFeatureB = ModelAPI_Feature::feature(*aFeatIt); - - AttributePtr anAttrStartA = - anOldFeatureA->attribute(anOldFeatureA->getKind() == SketchPlugin_Line::ID() ? SketchPlugin_Line::START_ID() : SketchPlugin_Arc::START_ID()); - AttributePtr anAttrEndA = - anOldFeatureA->attribute(anOldFeatureA->getKind() == SketchPlugin_Line::ID() ? SketchPlugin_Line::END_ID() : SketchPlugin_Arc::END_ID()); - AttributePtr anAttrStartB = - anOldFeatureB->attribute(anOldFeatureB->getKind() == SketchPlugin_Line::ID() ? SketchPlugin_Line::START_ID() : SketchPlugin_Arc::START_ID()); - AttributePtr anAttrEndB = - anOldFeatureB->attribute(anOldFeatureB->getKind() == SketchPlugin_Line::ID() ? SketchPlugin_Line::END_ID() : SketchPlugin_Arc::END_ID()); - - std::shared_ptr aBasePnt = std::dynamic_pointer_cast(anAttrBase)->pnt(); - std::shared_ptr aStartPntA = std::dynamic_pointer_cast(anAttrStartA)->pnt(); - std::shared_ptr anEndPntA = std::dynamic_pointer_cast(anAttrEndA)->pnt(); - std::shared_ptr aStartPntB = std::dynamic_pointer_cast(anAttrStartB)->pnt(); - std::shared_ptr anEndPntB = std::dynamic_pointer_cast(anAttrEndB)->pnt(); - double aDistBaseStartA = aBasePnt->distance(aStartPntA); - double aDistBaseEndA = aBasePnt->distance(anEndPntA); - double aDistStartAStartB = aStartPntA->distance(aStartPntB); - double aDistStartAEndB = aStartPntA->distance(anEndPntB); - double aDistEndAStartB = anEndPntA->distance(aStartPntB); - double aDistEndAEndB = anEndPntA->distance(anEndPntB); - - if((aDistBaseStartA < tolerance && (aDistStartAStartB < tolerance || aDistStartAEndB < tolerance)) || - (aDistBaseEndA < tolerance && (aDistEndAStartB < tolerance || aDistEndAStartB < tolerance))) { - return true; - } + AttributeRefAttrListPtr aBasePointsRefList = std::dynamic_pointer_cast( + aFilletFeature->attribute(SketchPlugin_Constraint::ENTITY_C())); + AttributeRefListPtr aResultEdgesRefList = std::dynamic_pointer_cast( + aFilletFeature->attribute(SketchPlugin_Constraint::ENTITY_B())); - // Check that point not on fillet arc - AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast( - aFeature->attribute(SketchPlugin_Constraint::ENTITY_B())); - std::list aNewFeatList = aRefListOfFillet->list(); - if(!aNewFeatList.empty()) { - aFeatIt = aNewFeatList.begin(); - FeaturePtr aNewArc; - //aNewFeatureA = ModelAPI_Feature::feature(*aFeatIt++); - //aNewFeatureB = ModelAPI_Feature::feature(*aFeatIt++); - aFeatIt++; aFeatIt++; - aNewArc = ModelAPI_Feature::feature(*aFeatIt); - AttributePtr anArcStart = aNewArc->attribute(SketchPlugin_Arc::START_ID()); - AttributePtr anArcEnd = aNewArc->attribute(SketchPlugin_Arc::END_ID()); - std::shared_ptr anArcStartPnt = std::dynamic_pointer_cast(anArcStart)->pnt(); - std::shared_ptr anArcEndPnt = std::dynamic_pointer_cast(anArcEnd)->pnt(); - double aDistBaseArcStart = aBasePnt->distance(anArcStartPnt); - double aDistBaseArcEnd = aBasePnt->distance(anArcEndPnt); - if(aDistBaseArcStart < tolerance || aDistBaseArcEnd < tolerance) { - return false; + std::list> aPointsList = aPointsRefList->list(); + for(std::list>::const_iterator anIt = aPointsList.cbegin(); anIt != aPointsList.cend(); anIt++) { + ObjectPtr anObject = (*anIt).first; + AttributePtr aPointAttribute = (*anIt).second; + + // If we alredy have some result then: + // - if it is the same point all ok, just skip it + // - if it is point on the fillet arc then it is not valid + if(aBasePointsRefList->size() > 0) { + if(aBasePointsRefList->isInList(aPointAttribute)) { + continue; } - } - } - const std::set& aRefsList = anAttrBase->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(anAttrBase == anAttrA) { - aCoincident = aConstrFeature; - break; + // Check that selected point not on the one of the result fillet arc. + for(int anIndex = 0; anIndex < aBasePointsRefList->size(); anIndex++) { + if(aResultEdgesRefList->size() > 0) { + FeaturePtr aResultArc; + aResultArc = ModelAPI_Feature::feature(aResultEdgesRefList->object(anIndex * 3 + 2)); + AttributePtr anArcStart = aResultArc->attribute(SketchPlugin_Arc::START_ID()); + AttributePtr anArcEnd = aResultArc->attribute(SketchPlugin_Arc::END_ID()); + std::shared_ptr anArcStartPnt = std::dynamic_pointer_cast(anArcStart)->pnt(); + std::shared_ptr anArcEndPnt = std::dynamic_pointer_cast(anArcEnd)->pnt(); + std::shared_ptr aSelectedPnt = std::dynamic_pointer_cast(aPointAttribute)->pnt(); + double aDistSelectedArcStart = aSelectedPnt->distance(anArcStartPnt); + double aDistSelectedArcEnd = aSelectedPnt->distance(anArcEndPnt); + if(aDistSelectedArcStart < tolerance || aDistSelectedArcEnd < tolerance) { + return false; + } } } - if(anAttrRefA.get() && !anAttrRefB->isObject()) { - AttributePtr anAttrB = anAttrRefB->attr(); - if(anAttrBase == anAttrB) { - aCoincident = aConstrFeature; - break; + } + + // Obtain constraint coincidence for the fillet point. + const std::set& aRefsList = aPointAttribute->owner()->data()->refsToMe(); + FeaturePtr aConstraintCoincidence; + for(std::set::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) { + std::shared_ptr aAttr = (*anIt); + 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(aPointAttribute == anAttrA) { + aConstraintCoincidence = aConstrFeature; + break; + } + } + if(anAttrRefB.get() && !anAttrRefB->isObject()) { + AttributePtr anAttrB = anAttrRefB->attr(); + if(aPointAttribute == anAttrB) { + aConstraintCoincidence = aConstrFeature; + break; + } } } } - } - - if(!aCoincident.get()) { - return false; - } - std::set aCoinsides; - SketchPlugin_Tools::findCoincidences(aCoincident, - SketchPlugin_ConstraintCoincidence::ENTITY_A(), - aCoinsides); - SketchPlugin_Tools::findCoincidences(aCoincident, - SketchPlugin_ConstraintCoincidence::ENTITY_B(), - aCoinsides); - // 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(!aConstraintCoincidence.get()) { + theError = "Error: one of the selected point does not have coicidence."; + return false; } - } - aCoinsides = aNewLines; - // Remove auxilary lines - if(aCoinsides.size() > 2) { - aNewLines.clear(); + // Get coincides from constraint. + std::set aCoinsides; + SketchPlugin_Tools::findCoincidences(aConstraintCoincidence, + SketchPlugin_ConstraintCoincidence::ENTITY_A(), + aCoinsides); + SketchPlugin_Tools::findCoincidences(aConstraintCoincidence, + SketchPlugin_ConstraintCoincidence::ENTITY_B(), + aCoinsides); + + // Remove points from set of coincides. + std::set aNewSetOfCoincides; for(std::set::iterator anIt = aCoinsides.begin(); anIt != aCoinsides.end(); ++anIt) { - if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) { - aNewLines.insert(*anIt); + if((*anIt)->getKind() == SketchPlugin_Line::ID() || + (*anIt)->getKind() == SketchPlugin_Arc::ID()) { + aNewSetOfCoincides.insert(*anIt); } } - aCoinsides = aNewLines; - } - - if(aCoinsides.size() != 2) { - return false; - } + aCoinsides = aNewSetOfCoincides; + + // If we still have more than two coincides remove auxilary entities from set of coincides. + if(aCoinsides.size() > 2) { + aNewSetOfCoincides.clear(); + for(std::set::iterator anIt = aCoinsides.begin(); anIt != aCoinsides.end(); ++anIt) { + if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) { + aNewSetOfCoincides.insert(*anIt); + } + } + aCoinsides = aNewSetOfCoincides; + } - // Check that lines not collinear - std::set::iterator anIt = aCoinsides.begin(); - FeaturePtr aFirstFeature = *anIt++; - FeaturePtr aSecondFeature = *anIt; - if(aFirstFeature->getKind() == SketchPlugin_Line::ID() && aSecondFeature->getKind() == SketchPlugin_Line::ID()) { - std::string aStartAttr = SketchPlugin_Line::START_ID(); - std::string anEndAttr = SketchPlugin_Line::END_ID(); - std::shared_ptr aFirstStartPnt, aFirstEndPnt, aSecondStartPnt, aSecondEndPnt; - aFirstStartPnt = std::dynamic_pointer_cast(aFirstFeature->attribute(aStartAttr))->pnt(); - aFirstEndPnt = std::dynamic_pointer_cast(aFirstFeature->attribute(anEndAttr))->pnt(); - aSecondStartPnt = std::dynamic_pointer_cast(aSecondFeature->attribute(aStartAttr))->pnt(); - aSecondEndPnt = std::dynamic_pointer_cast(aSecondFeature->attribute(anEndAttr))->pnt(); - double aCheck1 = abs((aFirstEndPnt->x() - aFirstStartPnt->x()) * (aSecondStartPnt->y() - aFirstStartPnt->y()) - - (aSecondStartPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y())); - double aCheck2 = abs((aFirstEndPnt->x() - aFirstStartPnt->x()) * (aSecondEndPnt->y() - aFirstStartPnt->y()) - - (aSecondEndPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y())); - if(aCheck1 < 1.e-7 && aCheck2 < 1.e-7) { + if(aCoinsides.size() != 2) { + theError = ("Error: One of the selected points does not have two suitable edges for fillet."); return false; } + + // Check that lines not collinear + std::set::iterator anIt = aCoinsides.begin(); + FeaturePtr aFirstFeature = *anIt++; + FeaturePtr aSecondFeature = *anIt; + if(aFirstFeature->getKind() == SketchPlugin_Line::ID() && aSecondFeature->getKind() == SketchPlugin_Line::ID()) { + std::string aStartAttr = SketchPlugin_Line::START_ID(); + std::string anEndAttr = SketchPlugin_Line::END_ID(); + std::shared_ptr aFirstStartPnt, aFirstEndPnt, aSecondStartPnt, aSecondEndPnt; + aFirstStartPnt = std::dynamic_pointer_cast(aFirstFeature->attribute(aStartAttr))->pnt(); + aFirstEndPnt = std::dynamic_pointer_cast(aFirstFeature->attribute(anEndAttr))->pnt(); + aSecondStartPnt = std::dynamic_pointer_cast(aSecondFeature->attribute(aStartAttr))->pnt(); + aSecondEndPnt = std::dynamic_pointer_cast(aSecondFeature->attribute(anEndAttr))->pnt(); + double aCheck1 = abs((aFirstEndPnt->x() - aFirstStartPnt->x()) * (aSecondStartPnt->y() - aFirstStartPnt->y()) - + (aSecondStartPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y())); + double aCheck2 = abs((aFirstEndPnt->x() - aFirstStartPnt->x()) * (aSecondEndPnt->y() - aFirstStartPnt->y()) - + (aSecondEndPnt->x() - aFirstStartPnt->x()) * (aFirstEndPnt->y() - aFirstStartPnt->y())); + if(aCheck1 < 1.e-7 && aCheck2 < 1.e-7) { + return false; + } + } } return true; diff --git a/src/SketchPlugin/SketchPlugin_Validators.h b/src/SketchPlugin/SketchPlugin_Validators.h index 1b38eb0e7..86f8ee295 100644 --- a/src/SketchPlugin/SketchPlugin_Validators.h +++ b/src/SketchPlugin/SketchPlugin_Validators.h @@ -170,7 +170,7 @@ class SketchPlugin_SolverErrorValidator : public ModelAPI_FeatureValidator */ class SketchPlugin_FilletVertexValidator : public ModelAPI_AttributeValidator { - public: +public: //! returns true if attribute is valid //! \param theAttribute the checked attribute //! \param theArguments arguments of the attribute (not used) diff --git a/src/SketchPlugin/Test/TestFillet.py b/src/SketchPlugin/Test/TestFillet.py index b4f648693..453a68891 100644 --- a/src/SketchPlugin/Test/TestFillet.py +++ b/src/SketchPlugin/Test/TestFillet.py @@ -1,13 +1,13 @@ """ TestFillet.py Unit test of SketchPlugin_ConstraintFillet class - + SketchPlugin_ConstraintFillet static const std::string MY_CONSTRAINT_FILLET_ID("SketchConstraintFillet"); data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId()); - data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId()); - data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId()); - data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId()); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefAttrList::typeId()); """ from GeomDataAPI import * @@ -20,28 +20,39 @@ import math aStartPoint1 = [] def createSketch1(theSketch): - global aStartPoint1 + global aEndPoint1, aEndPoint2 # Initialize sketch by two lines with coincident boundary allFeatures = [] # Line1 aSketchLine1 = theSketch.addFeature("SketchLine") aStartPoint1 = geomDataAPI_Point2D(aSketchLine1.attribute("StartPoint")) aEndPoint1 = geomDataAPI_Point2D(aSketchLine1.attribute("EndPoint")) - aStartPoint1.setValue(10., 5.) + aStartPoint1.setValue(-10., -10.) aEndPoint1.setValue(-10., 10.) allFeatures.append(aSketchLine1) # Line2 aSketchLine2 = theSketch.addFeature("SketchLine") aStartPoint2 = geomDataAPI_Point2D(aSketchLine2.attribute("StartPoint")) aEndPoint2 = geomDataAPI_Point2D(aSketchLine2.attribute("EndPoint")) - aStartPoint2.setValue(10., 5.) - aEndPoint2.setValue(15., 20.) + aStartPoint2.setValue(-10., 10.) + aEndPoint2.setValue(10., 10.) allFeatures.append(aSketchLine2) - # Coincidence - aCoincidence = theSketch.addFeature("SketchConstraintCoincidence") - aCoincidence.refattr("ConstraintEntityA").setAttr(aStartPoint1) - aCoincidence.refattr("ConstraintEntityB").setAttr(aStartPoint2) - + # Line3 + aSketchLine3 = theSketch.addFeature("SketchLine") + aStartPoint3 = geomDataAPI_Point2D(aSketchLine3.attribute("StartPoint")) + aEndPoint3 = geomDataAPI_Point2D(aSketchLine3.attribute("EndPoint")) + aStartPoint3.setValue(10., 10.) + aEndPoint3.setValue(10., -10.) + allFeatures.append(aSketchLine3) + # Coincidence1 + aCoincidence1 = theSketch.addFeature("SketchConstraintCoincidence") + aCoincidence1.refattr("ConstraintEntityA").setAttr(aEndPoint1) + aCoincidence1.refattr("ConstraintEntityB").setAttr(aStartPoint2) + # Coincidence2 + aCoincidence2 = theSketch.addFeature("SketchConstraintCoincidence") + aCoincidence2.refattr("ConstraintEntityA").setAttr(aEndPoint2) + aCoincidence2.refattr("ConstraintEntityB").setAttr(aStartPoint3) + theSketch.execute() return allFeatures @@ -70,10 +81,10 @@ def createSketch2(theSketch): aCoincidence = theSketch.addFeature("SketchConstraintCoincidence") aCoincidence.refattr("ConstraintEntityA").setAttr(aStartPoint1) aCoincidence.refattr("ConstraintEntityB").setAttr(aStartPoint2) - + theSketch.execute() return allFeatures - + def checkFillet(theObjects, theRadius): # Verify the arc and lines are connected smoothly print "Check Fillet" @@ -90,7 +101,7 @@ def checkFillet(theObjects, theRadius): aFilletArc = anArc[-1] assert(aFilletArc is not None) anArc.pop() - + anArcPoints = [] aPoint = geomDataAPI_Point2D(aFilletArc.attribute("ArcStartPoint")) print "ArcStartPoint " + repr(aPoint.x()) + " " + repr(aPoint.y()) @@ -103,20 +114,20 @@ def checkFillet(theObjects, theRadius): aCenterX = aPoint.x() aCenterY = aPoint.y() aFilletRadius = math.hypot(anArcPoints[0][0]-aCenterX, anArcPoints[0][1]-aCenterY) - + for line in aLine: aStartPoint = geomDataAPI_Point2D(line.attribute("StartPoint")) aEndPoint = geomDataAPI_Point2D(line.attribute("EndPoint")) - + aLinePoints = [] aLinePoints.append((aStartPoint.x(), aStartPoint.y())) print "aLineStartPoint " + repr(aStartPoint.x()) + " " + repr(aStartPoint.y()) aLinePoints.append((aEndPoint.x(), aEndPoint.y())) print "aLineEndPoint " + repr(aEndPoint.x()) + " " + repr(aEndPoint.y()) - + aLineDirX = aEndPoint.x() - aStartPoint.x() aLineDirY = aEndPoint.y() - aStartPoint.y() - + for arcPt in anArcPoints: for linePt in aLinePoints: if (math.hypot(linePt[0]-arcPt[0], linePt[1]-arcPt[1]) < 1.e-10): @@ -124,25 +135,26 @@ def checkFillet(theObjects, theRadius): aDirY = linePt[1] - aCenterY assert(math.fabs(math.hypot(aDirX, aDirY) - theRadius) < 1.e-7) aDot = aDirX * aLineDirX + aDirY * aLineDirY - + break; - for arc in anArc: - aStartPoint = geomDataAPI_Point2D(arc.attribute("ArcStartPoint")) - aEndPoint = geomDataAPI_Point2D(arc.attribute("ArcEndPoint")) - aCenterPoint = geomDataAPI_Point2D(arc.attribute("ArcCenter")) - - aBaseArcPoints = [] - aBaseArcPoints.append((aStartPoint.x(), aStartPoint.y())) - print "anArcStartPoint " + repr(aStartPoint.x()) + " " + repr(aStartPoint.y()) - aBaseArcPoints.append((aEndPoint.x(), aEndPoint.y())) - print "anArcEndPoint " + repr(aEndPoint.x()) + " " + repr(aEndPoint.y()) - print "anArcCenter " + repr(aCenterPoint.x()) + " " + repr(aCenterPoint.y()) - - aRadius = math.hypot(aStartPoint.x()-aCenterPoint.x(), aStartPoint.y()-aCenterPoint.y()) - aDist = math.hypot(aCenterPoint.x() - aCenterX, aCenterPoint.y() - aCenterY) - assert math.fabs(aFilletRadius + aRadius - aDist) < 1.e-7 or math.fabs(math.fabs(aFilletRadius - aRadius) - aDist) < 1.e-7, \ - "Fillet radius = {0}, Base arc radius = {1}, distance between centers = {2}".format(aFilletRadius, aRadius, aDist) + if (aSize == 3): + for arc in anArc: + aStartPoint = geomDataAPI_Point2D(arc.attribute("ArcStartPoint")) + aEndPoint = geomDataAPI_Point2D(arc.attribute("ArcEndPoint")) + aCenterPoint = geomDataAPI_Point2D(arc.attribute("ArcCenter")) + + aBaseArcPoints = [] + aBaseArcPoints.append((aStartPoint.x(), aStartPoint.y())) + print "anArcStartPoint " + repr(aStartPoint.x()) + " " + repr(aStartPoint.y()) + aBaseArcPoints.append((aEndPoint.x(), aEndPoint.y())) + print "anArcEndPoint " + repr(aEndPoint.x()) + " " + repr(aEndPoint.y()) + print "anArcCenter " + repr(aCenterPoint.x()) + " " + repr(aCenterPoint.y()) + + aRadius = math.hypot(aStartPoint.x()-aCenterPoint.x(), aStartPoint.y()-aCenterPoint.y()) + aDist = math.hypot(aCenterPoint.x() - aCenterX, aCenterPoint.y() - aCenterY) + assert math.fabs(aFilletRadius + aRadius - aDist) < 1.e-7 or math.fabs(math.fabs(aFilletRadius - aRadius) - aDist) < 1.e-7, \ + "Fillet radius = {0}, Base arc radius = {1}, distance between centers = {2}".format(aFilletRadius, aRadius, aDist) #========================================================================= @@ -182,10 +194,9 @@ FILLET_RADIUS2 = 5. #========================================================================= aSession.startOperation() aFillet = aSketchFeature.addFeature("SketchConstraintFillet") -aRefAttrA = aFillet.refattr("ConstraintEntityA"); -aResConstr = modelAPI_ResultConstruction(aFeaturesList[0].lastResult()) -assert(aResConstr) -aRefAttrA.setAttr(aStartPoint1) +aRefAttrA = aFillet.data().refattrlist("ConstraintEntityA"); +aRefAttrA.append(aEndPoint1) +aRefAttrA.append(aEndPoint2) aRadius = aFillet.real("ConstraintValue") aRadius.setValue(FILLET_RADIUS1) aFillet.execute() @@ -226,8 +237,8 @@ aSession.finishOperation() #========================================================================= aSession.startOperation() aFillet = aSketchFeature.addFeature("SketchConstraintFillet") -aRefAttrA = aFillet.refattr("ConstraintEntityA"); -aRefAttrA.setAttr(aStartPoint1) +aRefAttrA = aFillet.data().refattrlist("ConstraintEntityA"); +aRefAttrA.append(aStartPoint1) aRadius = aFillet.real("ConstraintValue") aRadius.setValue(FILLET_RADIUS1) aFillet.execute() diff --git a/src/SketchPlugin/plugin-Sketch.xml b/src/SketchPlugin/plugin-Sketch.xml index dbe93b228..98d339bc8 100644 --- a/src/SketchPlugin/plugin-Sketch.xml +++ b/src/SketchPlugin/plugin-Sketch.xml @@ -59,11 +59,17 @@ - - - + --> + + + + -- 2.39.2