X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FSketchPlugin%2FSketchPlugin_ConstraintFillet.cpp;h=40c4a83b379a1f6f035552196c7ebf1ffa97877c;hb=e6ab88ed62727ed0f267e133de8965c886d98d78;hp=23d1858b66b1d247a9da7c2f18a034e74c92f5dd;hpb=c65c7a084cf32f54c8d8a93fceace414c3b0fb21;p=modules%2Fshaper.git diff --git a/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp b/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp index 23d1858b6..40c4a83b3 100644 --- a/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp +++ b/src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp @@ -6,92 +6,88 @@ #include "SketchPlugin_ConstraintFillet.h" +#include #include +#include #include #include #include #include -#include #include +#include #include #include -#include #include +#include #include #include +#include #include #include #include #include +#include -#include - -#include #include +#include + +const double tolerance = 1.e-7; +const double paramTolerance = 1.e-4; + /// \brief Attract specified point on theNewArc to the attribute of theFeature static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute, FeaturePtr theFeature, const std::string& theFeatureAttribute); +/// \brief Calculates center of fillet arc and coordinates of tangency points +static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB, + double theRadius, bool theNotInversed[2], + std::shared_ptr& theCenter, + std::shared_ptr& theTangentA, + std::shared_ptr& theTangentB); + +/// Get point on 1/3 length of edge from fillet point +static void getPointOnEdge(const FeaturePtr theFeature, + const std::shared_ptr theFilletPoint, + std::shared_ptr& thePoint); + +/// Get distance from point to feature +static double getProjectionDistance(const FeaturePtr theFeature, + const std::shared_ptr thePoint); + +/// Get coincide edges for fillet +static std::set getCoincides(const FeaturePtr& theConstraintCoincidence); 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_AttributeRefAttr::typeId()); - data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::typeId()); - // initialize attribute not applicable for user - data()->attribute(SketchPlugin_Constraint::ENTITY_C())->setInitialized(); + data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId()); } void SketchPlugin_ConstraintFillet::execute() { std::shared_ptr aData = data(); - // 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())); - AttributeRefAttrPtr aBaseB = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Constraint::ENTITY_B())); - if (!aBaseA->isInitialized() || !aBaseB->isInitialized() || - !aBaseA->isObject() || !aBaseB->isObject()) - return; - // Check the fillet shapes is not initialized yet - AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast( - aData->attribute(SketchPlugin_Constraint::ENTITY_C())); - if (aRefListOfFillet->size() > 0) - return; - // Obtain features for the base objects - FeaturePtr aFeatureA, aFeatureB; - ResultConstructionPtr aRC = - std::dynamic_pointer_cast(aBaseA->object()); - if (aRC) aFeatureA = aRC->document()->feature(aRC); - aRC = std::dynamic_pointer_cast(aBaseB->object()); - if (aRC) aFeatureB = aRC->document()->feature(aRC); - if (!aFeatureA || !aFeatureB) + + // 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; + } - // Create list of objects composing a fillet - // copy aFeatureA - FeaturePtr aNewFeatureA = sketch()->addFeature(aFeatureA->getKind()); - aFeatureA->data()->copyTo(aNewFeatureA->data()); - aNewFeatureA->execute(); - aRefListOfFillet->append(aNewFeatureA); - // copy aFeatureB - FeaturePtr aNewFeatureB = sketch()->addFeature(aFeatureB->getKind()); - aFeatureB->data()->copyTo(aNewFeatureB->data()); - aNewFeatureB->execute(); - aRefListOfFillet->append(aNewFeatureB); - // create filleting arc - FeaturePtr aNewArc = sketch()->addFeature(SketchPlugin_Arc::ID()); - aRefListOfFillet->append(aNewArc); - aRefListOfFillet->setInitialized(); + // Get fillet radius. + double aFilletRadius = std::dynamic_pointer_cast( + aData->attribute(SketchPlugin_Constraint::VALUE()))->value(); // Wait all constraints being created, then send update events static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED); @@ -99,166 +95,540 @@ void SketchPlugin_ConstraintFillet::execute() if (isUpdateFlushed) Events_Loop::loop()->setFlushed(anUpdateEvent, false); - // Calculate arc attributes - static const int aNbFeatures = 2; - FeaturePtr aFeature[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 (aFeature[i]->getKind() == SketchPlugin_Line::ID()) { - aStartAttr = SketchPlugin_Line::START_ID(); - aEndAttr = SketchPlugin_Line::END_ID(); - } else if (aFeature[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); + 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; } - 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 i = 0; i < aNbFeatures; i++) { - int j = aNbFeatures; - for (; j < 2 * aNbFeatures; j++) - if (aStartEndPnt[i]->distance(aStartEndPnt[j]) < 1.e-10) { - isStart[0] = i==0; - isStart[1] = j==aNbFeatures; - break; + 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 (j < 2 * aNbFeatures) - break; - } - // tangent directions of the features - for (int i = 0; i < aNbFeatures; i++) { - std::shared_ptr aDir; - if (aFeature[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 (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) { - std::shared_ptr aCenterPoint = - std::dynamic_pointer_cast( - aFeature[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]) - 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; - - std::shared_ptr aSharedPoint = aStartEndPnt[isStart[0] ? 0 : 1]; - std::shared_ptr aBisect(new GeomAPI_Dir2d( - aTangentDir[0]->xy()->added(aTangentDir[1]->xy()))); - std::shared_ptr aStep = aBisect->xy()->multiplied(aFilletRadius); - std::shared_ptr aCenter = aSharedPoint->xy()->added(aStep); - std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue( - aCenter->x(), aCenter->y()); - std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::START_ID()))->setValue( - aCenter->x() - aStep->y(), aCenter->y() + aStep->x()); - std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::END_ID()))->setValue( - aCenter->x() + aStep->y(), aCenter->y() - aStep->x()); - aNewArc->execute(); - - // Create list of additional constraints: - // 1. Coincidence of boundary points of features 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(aFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd])); - recalculateAttributes(aNewArc, SketchPlugin_Arc::START_ID(), aFeature[aFeatInd], aFeatAttributes[anAttrInd]); - aConstraint->execute(); - 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(aFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd])); - recalculateAttributes(aNewArc, SketchPlugin_Arc::END_ID(), aFeature[aFeatInd], aFeatAttributes[anAttrInd]); - aConstraint->execute(); - ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); - // recalculate center of fillet arc - std::shared_ptr aStartPoint = std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::START_ID()))->pnt(); - std::shared_ptr aEndPoint = std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::END_ID()))->pnt(); - aCenter = aStartPoint->xy()->added(aEndPoint->xy())->multiplied(0.5); - std::dynamic_pointer_cast( - aNewArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue( - aCenter->x(), aCenter->y()); - // 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(); - 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(aNewArc->lastResult()); - aRefAttr = std::dynamic_pointer_cast( - aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B())); - bool isArc = aFeature[i]->getKind() == SketchPlugin_Arc::ID(); - aRefAttr->setObject(isArc ? aFeature[i]->lastResult() : aFeature[i]->firstResult()); - aConstraint->execute(); - ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent); + + 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; + + 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; + } + + // 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); + } + + // 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++) { + 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; + } + } + // tangent directions of the features + for (int i = 0; i < aNbFeatures; i++) { + 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(aResultArc->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(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(aResultArc->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(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 - ModelAPI_EventCreator::get()->sendUpdated(FeaturePtr(this), anUpdateEvent); - if (isUpdateFlushed) + // Send events to update the sub-features by the solver. + if(isUpdateFlushed) { Events_Loop::loop()->setFlushed(anUpdateEvent, true); - Events_Loop::loop()->flush(anUpdateEvent); - - // make base features auxiliary - static Events_ID aRedisplayEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY); - aFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); - aFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true); - ModelAPI_EventCreator::get()->sendUpdated(aFeatureA, aRedisplayEvent); - ModelAPI_EventCreator::get()->sendUpdated(aFeatureB, aRedisplayEvent); - Events_Loop::loop()->flush(aRedisplayEvent); + } +} + +void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID) +{ + if(theID == SketchPlugin_Constraint::ENTITY_A()) { + if(myListOfPointsChangedInCode) { + return; + } + + // 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; + } + + // 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(anAttrRefB.get()) { + AttributePtr anAttrB = anAttrRefB->attr(); + if(aFilletPointAttr == anAttrB) { + aConstraintCoincidence = aConstrFeature; + break; + } + } + } + } + + if(!aConstraintCoincidence.get()) { + myNewPoints.clear(); + setError("Error: No coincident edges at one of the selected points."); + return; + } + + // 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; + } + + if(aCoincides.size() != 2) { + myNewPoints.clear(); + setError("Error: One of the selected points does not have two suitable edges for fillet."); + return; + } + + // 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; + } + } + + // Set new default radius if it was not changed by user. + if(!myRadiusChangedByUser) { + myRadiusChangedInCode = true; + aRadiusAttribute->setValue(aMinimumRadius); + myRadiusChangedInCode = false; + } + + } else if(theID == SketchPlugin_Constraint::VALUE()) { + if(myRadiusInitialized && !myRadiusChangedInCode) { + myRadiusChangedByUser = true; + } + if(!myRadiusInitialized) { + myRadiusInitialized = true; + } + } } AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious) @@ -271,42 +641,424 @@ AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePreviou return anAIS; } +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, - FeaturePtr theFeature, const std::string& theFeatureAttribute) + FeaturePtr theFeature, const std::string& theFeatureAttribute) { std::shared_ptr anArcPoint = std::dynamic_pointer_cast( theNewArc->attribute(theNewArcAttribute))->pnt(); - if (theFeature->getKind() == SketchPlugin_Line::ID()) { - std::shared_ptr aStartPoint = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt(); - std::shared_ptr aEndPoint = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt(); - std::shared_ptr aLineDir(new GeomAPI_Dir2d( - aEndPoint->xy()->decreased(aStartPoint->xy()))); - std::shared_ptr aVec = anArcPoint->xy()->decreased(aStartPoint->xy()); - double aDot = aVec->dot(aLineDir->xy()); - aVec = aStartPoint->xy()->added(aLineDir->xy()->multiplied(aDot)); - anArcPoint->setX(aVec->x()); - anArcPoint->setY(aVec->y()); - } else if (theFeature->getKind() == SketchPlugin_Arc::ID()) { - std::shared_ptr aStartPoint = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt(); - std::shared_ptr aCenterPoint = std::dynamic_pointer_cast( - theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt(); - double aRadius = aStartPoint->distance(aCenterPoint); - std::shared_ptr aDir(new GeomAPI_Dir2d( - anArcPoint->xy()->decreased(aCenterPoint->xy()))); - std::shared_ptr aPoint = aCenterPoint->xy()->added(aDir->xy()->multiplied(aRadius)); - anArcPoint->setX(aPoint->x()); - anArcPoint->setY(aPoint->y()); - } else - return; - - std::dynamic_pointer_cast( - theNewArc->attribute(theNewArcAttribute))->setValue(anArcPoint->x(), anArcPoint->y()); std::dynamic_pointer_cast( theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y()); } + +/// \brief Find intersections of lines shifted along normal direction +void possibleFilletCenterLineLine( + std::shared_ptr thePointA, std::shared_ptr theDirA, + std::shared_ptr thePointB, std::shared_ptr theDirB, + double theRadius, std::list< std::shared_ptr >& theCenters) +{ + std::shared_ptr aDirAT(new GeomAPI_Dir2d(-theDirA->y(), theDirA->x())); + std::shared_ptr aDirBT(new GeomAPI_Dir2d(-theDirB->y(), theDirB->x())); + std::shared_ptr aPntA, aPntB; + double aDet = theDirA->cross(theDirB); + for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) { + aPntA = thePointA->added(aDirAT->xy()->multiplied(aStepA * theRadius)); + for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) { + aPntB = thePointB->added(aDirBT->xy()->multiplied(aStepB * theRadius)); + double aVX = aDirAT->xy()->dot(aPntA); + double aVY = aDirBT->xy()->dot(aPntB); + std::shared_ptr aPoint(new GeomAPI_XY( + (theDirB->x() * aVX - theDirA->x() * aVY) / aDet, + (theDirB->y() * aVX - theDirA->y() * aVY) / aDet)); + theCenters.push_back(aPoint); + } + } +} + +/// \brief Find intersections of line shifted along normal direction in both sides +/// and a circle with extended radius +void possibleFilletCenterLineArc( + std::shared_ptr theStartLine, std::shared_ptr theDirLine, + std::shared_ptr theCenterArc, double theRadiusArc, + double theRadius, std::list< std::shared_ptr >& theCenters) +{ + std::shared_ptr aDirT(new GeomAPI_Dir2d(-theDirLine->y(), theDirLine->x())); + std::shared_ptr aPnt; + double aDirNorm2 = theDirLine->dot(theDirLine); + double aRad = 0.0; + double aDirX = theDirLine->x(); + double aDirX2 = theDirLine->x() * theDirLine->x(); + double aDirY2 = theDirLine->y() * theDirLine->y(); + double aDirXY = theDirLine->x() * theDirLine->y(); + for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) { + aPnt = theStartLine->added(aDirT->xy()->multiplied(aStepA * theRadius)); + double aCoeff = aDirT->xy()->dot(aPnt->decreased(theCenterArc)); + double aCoeff2 = aCoeff * aCoeff; + for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) { + aRad = theRadiusArc + aStepB * theRadius; + double aD = aRad * aRad * aDirNorm2 - aCoeff2; + if (aD < 0.0) + continue; + double aDs = sqrt(aD); + double x1 = theCenterArc->x() + (aCoeff * aDirT->x() - aDirT->y() * aDs) / aDirNorm2; + double x2 = theCenterArc->x() + (aCoeff * aDirT->x() + aDirT->y() * aDs) / aDirNorm2; + double y1 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() - + aDirXY * (aPnt->x() - theCenterArc->x()) - theDirLine->y() * aDs) / aDirNorm2; + double y2 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() - + aDirXY * (aPnt->x() - theCenterArc->x()) + theDirLine->y() * aDs) / aDirNorm2; + + std::shared_ptr aPoint1(new GeomAPI_XY(x1, y1)); + theCenters.push_back(aPoint1); + std::shared_ptr aPoint2(new GeomAPI_XY(x2, y2)); + theCenters.push_back(aPoint2); + } + } +} + +/// \brief Find intersections of two circles with extended radii +void possibleFilletCenterArcArc( + std::shared_ptr theCenterA, double theRadiusA, + std::shared_ptr theCenterB, double theRadiusB, + double theRadius, std::list< std::shared_ptr >& theCenters) +{ + std::shared_ptr aCenterDir = theCenterB->decreased(theCenterA); + double aCenterDist2 = aCenterDir->dot(aCenterDir); + double aCenterDist = sqrt(aCenterDist2); + + double aRadA, aRadB; + for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) { + aRadA = theRadiusA + aStepA * theRadius; + for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) { + aRadB = theRadiusB + aStepB * theRadius; + if (aRadA + aRadB < aCenterDist || fabs(aRadA - aRadB) > aCenterDist) + continue; // there is no intersections + + double aMedDist = (aRadA * aRadA - aRadB * aRadB + aCenterDist2) / (2.0 * aCenterDist); + double aHeight = sqrt(aRadA * aRadA - aMedDist * aMedDist); + + double x1 = theCenterA->x() + (aMedDist * aCenterDir->x() + aCenterDir->y() * aHeight) / aCenterDist; + double y1 = theCenterA->y() + (aMedDist * aCenterDir->y() - aCenterDir->x() * aHeight) / aCenterDist; + + double x2 = theCenterA->x() + (aMedDist * aCenterDir->x() - aCenterDir->y() * aHeight) / aCenterDist; + double y2 = theCenterA->y() + (aMedDist * aCenterDir->y() + aCenterDir->x() * aHeight) / aCenterDist; + + std::shared_ptr aPoint1(new GeomAPI_XY(x1, y1)); + theCenters.push_back(aPoint1); + std::shared_ptr aPoint2(new GeomAPI_XY(x2, y2)); + theCenters.push_back(aPoint2); + } + } +} + +void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB, + double theRadius, bool theNotInversed[2], + std::shared_ptr& theCenter, + std::shared_ptr& theTangentA, + std::shared_ptr& theTangentB) +{ + static const int aNbFeatures = 2; + FeaturePtr aFeature[aNbFeatures] = {theFeatureA, theFeatureB}; + std::shared_ptr aStart[aNbFeatures], aEnd[aNbFeatures], aCenter[aNbFeatures]; + std::shared_ptr aStartPoint, aEndPoint; + + for (int i = 0; i < aNbFeatures; i++) { + if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) { + aStartPoint = std::dynamic_pointer_cast( + aFeature[i]->attribute(SketchPlugin_Line::START_ID())); + aEndPoint = std::dynamic_pointer_cast( + aFeature[i]->attribute(SketchPlugin_Line::END_ID())); + } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) { + aStartPoint = std::dynamic_pointer_cast( + aFeature[i]->attribute(SketchPlugin_Arc::START_ID())); + aEndPoint = std::dynamic_pointer_cast( + aFeature[i]->attribute(SketchPlugin_Arc::END_ID())); + aCenter[i] = std::dynamic_pointer_cast( + aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt()->xy(); + } else + return; + aStart[i] = std::shared_ptr(theNotInversed[i] ? + new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()) : + new GeomAPI_XY(aEndPoint->x(), aEndPoint->y())); + aEnd[i] = std::shared_ptr(theNotInversed[i] ? + new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()) : + new GeomAPI_XY(aStartPoint->x(), aStartPoint->y())); + } + + if (theFeatureA->getKind() == SketchPlugin_Line::ID() && + theFeatureB->getKind() == SketchPlugin_Line::ID()) { + std::shared_ptr aDir[2]; + std::shared_ptr aDirT[2]; + for (int i = 0; i < aNbFeatures; i++) { + aDir[i] = std::shared_ptr(new GeomAPI_Dir2d(aEnd[i]->decreased(aStart[i]))); + aDirT[i] = std::shared_ptr(new GeomAPI_Dir2d(-aDir[i]->y(), aDir[i]->x())); + } + + // get and filter possible centers + std::list< std::shared_ptr > aSuspectCenters; + possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1], theRadius, aSuspectCenters); + double aDot = 0.0; + std::list< std::shared_ptr >::iterator anIt = aSuspectCenters.begin(); + for (; anIt != aSuspectCenters.end(); anIt++) { + aDot = aDirT[0]->xy()->dot(aStart[0]->decreased(*anIt)); + theTangentA = (*anIt)->added(aDirT[0]->xy()->multiplied(aDot)); + if (theTangentA->decreased(aStart[0])->dot(aDir[0]->xy()) < 0.0) + continue; // incorrect position + aDot = aDirT[1]->xy()->dot(aStart[1]->decreased(*anIt)); + theTangentB = (*anIt)->added(aDirT[1]->xy()->multiplied(aDot)); + if (theTangentB->decreased(aStart[1])->dot(aDir[1]->xy()) < 0.0) + continue; // incorrect position + // the center is found, stop searching + theCenter = *anIt; + return; + } + } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() && + theFeatureB->getKind() == SketchPlugin_Line::ID()) || + (theFeatureA->getKind() == SketchPlugin_Line::ID() && + theFeatureB->getKind() == SketchPlugin_Arc::ID())) { + int aLineInd = theFeatureA->getKind() == SketchPlugin_Line::ID() ? 0 : 1; + double anArcRadius = aStart[1-aLineInd]->distance(aCenter[1-aLineInd]); + std::shared_ptr aDirLine = std::shared_ptr( + new GeomAPI_Dir2d(aEnd[aLineInd]->decreased(aStart[aLineInd]))); + std::shared_ptr aDirT = std::shared_ptr( + new GeomAPI_Dir2d(-aDirLine->y(), aDirLine->x())); + + std::shared_ptr aStartArcDir = std::shared_ptr( + new GeomAPI_Dir2d(aStart[1-aLineInd]->decreased(aCenter[1-aLineInd]))); + std::shared_ptr aEndArcDir = std::shared_ptr( + new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd]))); + double anArcAngle = aEndArcDir->angle(aStartArcDir); + + // get possible centers and filter them + std::list< std::shared_ptr > aSuspectCenters; + possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], anArcRadius, theRadius, aSuspectCenters); + double aDot = 0.0; + // the line is forward into the arc + double innerArc = aCenter[1-aLineInd]->decreased(aStart[aLineInd])->dot(aDirLine->xy()); + std::shared_ptr aLineTgPoint, anArcTgPoint; + // The possible centers are ranged by their positions. + // If the point is not satisfy one of criteria, the weight is decreased with penalty. + int aBestWeight = 0; + std::list< std::shared_ptr >::iterator anIt = aSuspectCenters.begin(); + for (; anIt != aSuspectCenters.end(); anIt++) { + int aWeight = 2; + aDot = aDirT->xy()->dot(aStart[aLineInd]->decreased(*anIt)); + aLineTgPoint = (*anIt)->added(aDirT->xy()->multiplied(aDot)); + // Check the point is placed on the correct arc (penalty if false) + if (aCenter[1-aLineInd]->distance(*anIt) * innerArc > anArcRadius * innerArc) + aWeight -= 1; + std::shared_ptr aCurDir = std::shared_ptr( + new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1-aLineInd]))); + double aCurAngle = aCurDir->angle(aStartArcDir); + if (anArcAngle < 0.0) aCurAngle *= -1.0; + if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle)) + continue; + if (aWeight > aBestWeight) + aBestWeight = aWeight; + else if (aWeight < aBestWeight || + aStart[aLineInd]->distance(*anIt) > + aStart[aLineInd]->distance(theCenter)) // <-- take closer point + continue; + // the center is found, stop searching + theCenter = *anIt; + anArcTgPoint = aCenter[1-aLineInd]->added(aCurDir->xy()->multiplied(anArcRadius)); + if (theFeatureA->getKind() == SketchPlugin_Line::ID()) { + theTangentA = aLineTgPoint; + theTangentB = anArcTgPoint; + } else { + theTangentA = anArcTgPoint; + theTangentB = aLineTgPoint; + } + //return; + } + } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() && + theFeatureB->getKind() == SketchPlugin_Arc::ID()) { + double anArcRadius[aNbFeatures]; + double anArcAngle[aNbFeatures]; + std::shared_ptr aStartArcDir[aNbFeatures]; + for (int i = 0; i < aNbFeatures; i++) { + anArcRadius[i] = aStart[i]->distance(aCenter[i]); + aStartArcDir[i] = std::shared_ptr( + new GeomAPI_Dir2d(aStart[i]->decreased(aCenter[i]))); + std::shared_ptr aEndArcDir = std::shared_ptr( + new GeomAPI_Dir2d(aEnd[i]->decreased(aCenter[i]))); + anArcAngle[i] = aEndArcDir->angle(aStartArcDir[i]); + } + + // get and filter possible centers + std::list< std::shared_ptr > aSuspectCenters; + possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1], anArcRadius[1], theRadius, aSuspectCenters); + double aDot = 0.0; + std::shared_ptr aLineTgPoint, anArcTgPoint; + std::list< std::shared_ptr >::iterator anIt = aSuspectCenters.begin(); + for (; anIt != aSuspectCenters.end(); anIt++) { + std::shared_ptr aCurDir = std::shared_ptr( + new GeomAPI_Dir2d((*anIt)->decreased(aCenter[0]))); + double aCurAngle = aCurDir->angle(aStartArcDir[0]); + if (anArcAngle[0] < 0.0) aCurAngle *= -1.0; + if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[0])) + continue; // incorrect position + theTangentA = aCenter[0]->added(aCurDir->xy()->multiplied(anArcRadius[0])); + + aCurDir = std::shared_ptr(new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1]))); + aCurAngle = aCurDir->angle(aStartArcDir[1]); + if (anArcAngle[1] < 0.0) aCurAngle *= -1.0; + if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[1])) + continue; // incorrect position + theTangentB = aCenter[1]->added(aCurDir->xy()->multiplied(anArcRadius[1])); + + // the center is found, stop searching + theCenter = *anIt; + return; + } + } +} + +void getPointOnEdge(const FeaturePtr theFeature, + const std::shared_ptr theFilletPoint, + std::shared_ptr& thePoint) { + if(theFeature->getKind() == SketchPlugin_Line::ID()) { + std::shared_ptr aPntStart = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt(); + std::shared_ptr aPntEnd = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt(); + if(aPntStart->distance(theFilletPoint) > 1.e-7) { + aPntStart = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt(); + aPntEnd = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt(); + } + thePoint.reset( new GeomAPI_Pnt2d(aPntStart->xy()->added( aPntEnd->xy()->decreased( aPntStart->xy() )->multiplied(1.0 / 3.0) ) ) ); + } else { + std::shared_ptr aPntTemp; + std::shared_ptr aPntStart = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt(); + std::shared_ptr aPntEnd = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt(); + if(theFeature->attribute(SketchPlugin_Arc::INVERSED_ID())) { + aPntTemp = aPntStart; + aPntStart = aPntEnd; + aPntEnd = aPntTemp; + } + std::shared_ptr aCenterPnt = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt(); + std::shared_ptr aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart)); + double aStartParameter(0), anEndParameter(0); + aCirc->parameter(aPntStart, paramTolerance, aStartParameter); + aCirc->parameter(aPntEnd, paramTolerance, anEndParameter); + if(aPntStart->distance(theFilletPoint) > tolerance) { + double aTmpParameter = aStartParameter; + aStartParameter = anEndParameter; + anEndParameter = aTmpParameter; + } + double aPntParameter = aStartParameter + (anEndParameter - aStartParameter) / 3.0; + aCirc->D0(aPntParameter, thePoint); + } +} + +double getProjectionDistance(const FeaturePtr theFeature, + const std::shared_ptr thePoint) +{ + std::shared_ptr aProjectPnt; + if(theFeature->getKind() == SketchPlugin_Line::ID()) { + std::shared_ptr aPntStart = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt(); + std::shared_ptr aPntEnd = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt(); + std::shared_ptr aLin(new GeomAPI_Lin2d(aPntStart, aPntEnd)); + aProjectPnt = aLin->project(thePoint); + } else { + std::shared_ptr aPntStart = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt(); + std::shared_ptr aPntEnd = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt(); + std::shared_ptr aCenterPnt = std::dynamic_pointer_cast( + theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt(); + std::shared_ptr aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart)); + aProjectPnt = aCirc->project(thePoint); + } + if(aProjectPnt.get()) { + return aProjectPnt->distance(thePoint); + } + return -1; +} + +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; +}