Salome HOME
Add copyright header according to request of CEA from 06.06.2017
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Fillet.cpp
1 // Copyright (C) 2014-2017  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
18 //
19
20 #include "SketchPlugin_Fillet.h"
21
22 #include "SketchPlugin_Arc.h"
23 #include "SketchPlugin_Line.h"
24 #include "SketchPlugin_Point.h"
25 #include "SketchPlugin_Sketch.h"
26 #include "SketchPlugin_ConstraintEqual.h"
27 #include "SketchPlugin_ConstraintCoincidence.h"
28 #include "SketchPlugin_ConstraintLength.h"
29 #include "SketchPlugin_ConstraintMiddle.h"
30 #include "SketchPlugin_ConstraintTangent.h"
31 #include "SketchPlugin_ConstraintRadius.h"
32 #include "SketchPlugin_Tools.h"
33
34 #include <ModelAPI_AttributeRefAttr.h>
35 #include <ModelAPI_Data.h>
36 #include <ModelAPI_Events.h>
37 #include <ModelAPI_Session.h>
38 #include <ModelAPI_Tools.h>
39 #include <ModelAPI_Validator.h>
40
41 #include <GeomAlgoAPI_Circ2dBuilder.h>
42 #include <GeomAlgoAPI_EdgeBuilder.h>
43
44 #include <GeomAPI_Circ2d.h>
45 #include <GeomAPI_Dir2d.h>
46 #include <GeomAPI_Lin2d.h>
47 #include <GeomAPI_Pnt2d.h>
48 #include <GeomAPI_XY.h>
49
50 #include <GeomDataAPI_Point2D.h>
51
52 #include <Events_Loop.h>
53
54 #include <math.h>
55
56 const double tolerance = 1.e-7;
57 const double paramTolerance = 1.e-4;
58 const double PI = 3.141592653589793238463;
59
60 /// \brief Attract specified point on theNewArc to the attribute of theFeature
61 static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
62   FeaturePtr theFeature, const std::string& theFeatureAttribute);
63
64
65 /// \brief Calculate radius of a fillet.
66 ///        It should not be greater than 1/3 of shortest edge length.
67 static double calculateFilletRadius(FeaturePtr theFilletFeatures[2]);
68
69 /// \brief Calculates center of fillet arc and coordinates of tangency points
70 static void calculateFilletCenter(FeaturePtr theFilletFeatures[2],
71                                   double theFilletRadius,
72                                   const std::shared_ptr<GeomAPI_Ax3>& theSketchPlane,
73                                   std::shared_ptr<GeomAPI_XY>& theCenter,
74                                   std::shared_ptr<GeomAPI_XY>& theTangentA,
75                                   std::shared_ptr<GeomAPI_XY>& theTangentB);
76
77 /// Get coincide edges for fillet
78 static std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence);
79
80 static std::set<FeaturePtr> findFeaturesToRemove(const FeaturePtr theFeature,
81                                                  const AttributePtr theAttribute);
82
83 SketchPlugin_Fillet::SketchPlugin_Fillet()
84 : myFilletCreated(false)
85 {
86 }
87
88 void SketchPlugin_Fillet::initAttributes()
89 {
90   data()->addAttribute(FILLET_POINT_ID(), ModelAPI_AttributeRefAttr::typeId());
91 }
92
93 void SketchPlugin_Fillet::execute()
94 {
95   // Wait all constraints being created, then send update events
96   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
97   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
98   if (isUpdateFlushed)
99     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
100
101   // set flag here to avoid building Fillet presentation if "Redisplay" event appears
102   myFilletCreated = true;
103
104   // Calculate Fillet parameters if does not yet
105   if (!myBaseFeatures[0] || !myBaseFeatures[1])
106     calculateFilletParameters();
107
108   // Create arc feature.
109   FeaturePtr aFilletArc = sketch()->addFeature(SketchPlugin_Arc::ID());
110
111   // Set arc attributes.
112   bool aWasBlocked = aFilletArc->data()->blockSendAttributeUpdated(true);
113   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
114       aFilletArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(myCenterXY->x(),
115                                                                       myCenterXY->y());
116   std::shared_ptr<GeomDataAPI_Point2D> aStartPoint =
117       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
118           aFilletArc->attribute(SketchPlugin_Arc::START_ID()));
119   std::shared_ptr<GeomDataAPI_Point2D> aEndPoint =
120       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
121           aFilletArc->attribute(SketchPlugin_Arc::END_ID()));
122   if(aStartPoint->isInitialized() && aEndPoint->isInitialized()
123       && (aStartPoint->pnt()->xy()->distance(myTangentXY1) > tolerance
124       || aEndPoint->pnt()->xy()->distance(myTangentXY2) > tolerance)) {
125     std::dynamic_pointer_cast<SketchPlugin_Arc>(aFilletArc)->setReversed(false);
126   }
127   aStartPoint->setValue(myTangentXY1->x(), myTangentXY1->y());
128   aEndPoint->setValue(myTangentXY2->x(), myTangentXY2->y());
129   aFilletArc->data()->blockSendAttributeUpdated(aWasBlocked);
130   aFilletArc->execute();
131
132   // Delete features with refs to points of edges.
133   std::shared_ptr<GeomDataAPI_Point2D> aStartPoint1;
134   int aFeatInd1 = myIsReversed ? 1 : 0;
135   int anAttrInd1 = (myIsReversed ? 2 : 0) + (myIsNotInversed[aFeatInd1] ? 0 : 1);
136   aStartPoint1 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
137       myBaseFeatures[aFeatInd1]->attribute(myFeatAttributes[anAttrInd1]));
138   std::set<FeaturePtr> aFeaturesToBeRemoved1 =
139     findFeaturesToRemove(myBaseFeatures[aFeatInd1], aStartPoint1);
140
141   std::shared_ptr<GeomDataAPI_Point2D> aStartPoint2;
142   int aFeatInd2 = myIsReversed ? 0 : 1;
143   int anAttrInd2 = (myIsReversed ? 0 : 2) + (myIsNotInversed[aFeatInd2] ? 0 : 1);
144   aStartPoint2 = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
145       myBaseFeatures[aFeatInd2]->attribute(myFeatAttributes[anAttrInd2]));
146   std::set<FeaturePtr> aFeaturesToBeRemoved2 =
147     findFeaturesToRemove(myBaseFeatures[aFeatInd2], aStartPoint2);
148
149   aFeaturesToBeRemoved1.insert(aFeaturesToBeRemoved2.begin(), aFeaturesToBeRemoved2.end());
150   ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved1);
151   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
152
153   // Update fillet edges.
154   recalculateAttributes(aFilletArc, SketchPlugin_Arc::START_ID(),
155                         myBaseFeatures[aFeatInd1], myFeatAttributes[anAttrInd1]);
156   recalculateAttributes(aFilletArc, SketchPlugin_Arc::END_ID(),
157                         myBaseFeatures[aFeatInd2], myFeatAttributes[anAttrInd2]);
158
159   // Create coincidence features.
160   FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
161   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
162       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
163   aRefAttr->setAttr(aFilletArc->attribute(SketchPlugin_Arc::START_ID()));
164   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
165       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
166   aRefAttr->setAttr(myBaseFeatures[aFeatInd1]->attribute(myFeatAttributes[anAttrInd1]));
167   aConstraint->execute();
168   ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
169   aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
170   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
171       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
172   aRefAttr->setAttr(aFilletArc->attribute(SketchPlugin_Arc::END_ID()));
173   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
174       aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
175   aRefAttr->setAttr(myBaseFeatures[aFeatInd2]->attribute(myFeatAttributes[anAttrInd2]));
176   aConstraint->execute();
177   ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
178
179   // Create tangent features.
180   for (int i = 0; i < 2; i++) {
181     aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
182     aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
183         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
184     aRefAttr->setObject(aFilletArc->lastResult());
185     aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
186         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
187     bool isArc = myBaseFeatures[i]->getKind() == SketchPlugin_Arc::ID();
188     aRefAttr->setObject(isArc ? myBaseFeatures[i]->lastResult() :
189                                 myBaseFeatures[i]->firstResult());
190     aConstraint->execute();
191     ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
192   }
193
194   // Send events to update the sub-features by the solver.
195   if(isUpdateFlushed) {
196     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
197   }
198 }
199
200 AISObjectPtr SketchPlugin_Fillet::getAISObject(AISObjectPtr thePrevious)
201 {
202   if(myFilletCreated) {
203     return AISObjectPtr();
204   }
205
206   SketchPlugin_Sketch* aSketch = sketch();
207   if(!aSketch) {
208     return AISObjectPtr();
209   }
210
211   if (!calculateFilletParameters())
212     return AISObjectPtr();
213
214   // Create arc for presentation.
215   std::shared_ptr<GeomAPI_Pnt> aCenterPnt(aSketch->to3D(myCenterXY->x(), myCenterXY->y()));
216   std::shared_ptr<GeomAPI_Pnt> aTangentPnt1(aSketch->to3D(myTangentXY1->x(),
217                                                           myTangentXY1->y()));
218   std::shared_ptr<GeomAPI_Pnt> aTangentPnt2(aSketch->to3D(myTangentXY2->x(),
219                                                           myTangentXY2->y()));
220   std::shared_ptr<GeomDataAPI_Dir> aNDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
221     aSketch->data()->attribute(SketchPlugin_Sketch::NORM_ID()));
222   std::shared_ptr<GeomAPI_Shape> anArcShape =
223       GeomAlgoAPI_EdgeBuilder::lineCircleArc(aCenterPnt, aTangentPnt1, aTangentPnt2, aNDir->dir());
224
225   AISObjectPtr anAISObject = thePrevious;
226   if(!anAISObject.get()) {
227     anAISObject = AISObjectPtr(new GeomAPI_AISObject);
228   }
229   anAISObject->createShape(anArcShape);
230   return anAISObject;
231 }
232
233 bool SketchPlugin_Fillet::calculateFilletParameters()
234 {
235   // Get fillet point.
236   AttributeRefAttrPtr aPointRefAttr = refattr(FILLET_POINT_ID());
237   if (!aPointRefAttr->isInitialized() || aPointRefAttr->isObject())
238     return false;
239   std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2D =
240     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointRefAttr->attr());
241   if (!aFilletPoint2D.get())
242     return false;
243
244   if (!findFeaturesContainingFilletPoint(aFilletPoint2D)) {
245     setError("Error: Selected point does not have two suitable edges for fillet.");
246     return false;
247   }
248
249   std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
250   double aRadius = calculateFilletRadius(myBaseFeatures);
251
252   // Calculate arc attributes.
253   static const int aNbFeatures = 2;
254   // First pair of points relate to first feature, second pair -  to second.
255   std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2];
256   for (int i = 0; i < aNbFeatures; i++) {
257     std::string aStartAttr, aEndAttr;
258     if (myBaseFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
259       aStartAttr = SketchPlugin_Line::START_ID();
260       aEndAttr = SketchPlugin_Line::END_ID();
261     } else if (myBaseFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
262       aStartAttr = SketchPlugin_Arc::START_ID();
263       aEndAttr = SketchPlugin_Arc::END_ID();
264     } else { // Wrong argument.
265       setError("Error: One of the points has wrong coincide feature");
266       return false;
267     }
268     myFeatAttributes[2*i] = aStartAttr;
269     aStartEndPnt[2*i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
270       myBaseFeatures[i]->attribute(aStartAttr))->pnt();
271     myFeatAttributes[2*i+1] = aEndAttr;
272     aStartEndPnt[2*i+1] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
273       myBaseFeatures[i]->attribute(aEndAttr))->pnt();
274   }
275   for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) {
276     for (int j = 0; j < 2; j++) // loop on start-end of each feature
277       if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPnt2d) < 1.e-10) {
278         myIsNotInversed[aFeatInd] = (j==0);
279         break;
280       }
281   }
282
283   std::shared_ptr<GeomAPI_Ax3> aSketchPlane = SketchPlugin_Sketch::plane(sketch());
284   calculateFilletCenter(myBaseFeatures, aRadius, aSketchPlane,
285                         myCenterXY, myTangentXY1, myTangentXY2);
286
287   // Tangent directions of the features in coincident point.
288   std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures];
289   for (int i = 0; i < aNbFeatures; i++) {
290     std::shared_ptr<GeomAPI_XY> aDir;
291     if (myBaseFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
292       aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy());
293       if (!myIsNotInversed[i])
294         aDir = aDir->multiplied(-1.0);
295     } else if (myBaseFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
296       std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint =
297         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
298         myBaseFeatures[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
299       aDir = myIsNotInversed[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy();
300       aDir = aDir->decreased(aCenterPoint->xy());
301
302       double x = aDir->x();
303       double y = aDir->y();
304       aDir->setX(-y);
305       aDir->setY(x);
306       if (myIsNotInversed[i] ==
307           std::dynamic_pointer_cast<SketchPlugin_Arc>(myBaseFeatures[i])->isReversed())
308         aDir = aDir->multiplied(-1.0);
309     }
310     aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
311   }
312
313   // By default, the start point of fillet arc is connected to FeatureA,
314   // and the end point - to FeatureB. But when the angle between TangentDirA and
315   // TangentDirB greater 180 degree, the sequaence of features need to be reversed.
316   double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A),
317   // where A and B - angles between corresponding tanget direction and the X axis
318   myIsReversed = cosBA > 0.0;
319
320   if(myIsReversed) {
321     std::shared_ptr<GeomAPI_XY> aTmp = myTangentXY1;
322     myTangentXY1 = myTangentXY2;
323     myTangentXY2 = aTmp;
324   }
325   return true;
326 }
327
328 bool SketchPlugin_Fillet::findFeaturesContainingFilletPoint(
329     std::shared_ptr<GeomDataAPI_Point2D> theFilletPoint)
330 {
331   // Obtain constraint coincidence for the fillet point.
332   FeaturePtr aConstraintCoincidence;
333   const std::set<AttributePtr>& aRefsList = theFilletPoint->owner()->data()->refsToMe();
334   for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin();
335       anIt != aRefsList.cend();
336       ++anIt) {
337     std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
338     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
339     if(aFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
340       AttributeRefAttrPtr anAttrRefA =
341           aFeature->refattr(SketchPlugin_ConstraintCoincidence::ENTITY_A());
342       AttributeRefAttrPtr anAttrRefB =
343           aFeature->refattr(SketchPlugin_ConstraintCoincidence::ENTITY_B());
344       if(anAttrRefA.get() && !anAttrRefA->isObject()) {
345         AttributePtr anAttrA = anAttrRefA->attr();
346         if(theFilletPoint == anAttrA) {
347           aConstraintCoincidence = aFeature;
348           break;
349         }
350       }
351       if(anAttrRefB.get() && !anAttrRefB->isObject()) {
352         AttributePtr anAttrB = anAttrRefB->attr();
353         if(theFilletPoint == anAttrB) {
354           aConstraintCoincidence = aFeature;
355           break;
356         }
357       }
358     }
359   }
360
361   if(!aConstraintCoincidence.get())
362     return false;
363
364   // Get coincide edges.
365   std::set<FeaturePtr> anEdgeFeatures = getCoincides(aConstraintCoincidence);
366   std::set<FeaturePtr>::iterator aLinesIt = anEdgeFeatures.begin();
367   for (int i = 0; aLinesIt != anEdgeFeatures.end() && i < 2; ++aLinesIt, ++i)
368     myBaseFeatures[i] = *aLinesIt;
369
370   return myBaseFeatures[0] && myBaseFeatures[1];
371 }
372
373 // =========   Auxiliary functions   =================
374 void recalculateAttributes(FeaturePtr theNewArc,  const std::string& theNewArcAttribute,
375                            FeaturePtr theFeature, const std::string& theFeatureAttribute)
376 {
377   std::shared_ptr<GeomAPI_Pnt2d> anArcPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
378       theNewArc->attribute(theNewArcAttribute))->pnt();
379   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
380       theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
381 }
382
383 static std::shared_ptr<GeomAPI_Pnt2d> toPoint(const AttributePtr& theAttribute)
384 {
385   std::shared_ptr<GeomAPI_Pnt2d> aPoint;
386   AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
387   if (aPointAttr)
388     aPoint = aPointAttr->pnt();
389   return aPoint;
390 }
391
392 static std::shared_ptr<GeomAPI_Lin2d> toLine(const FeaturePtr& theFeature)
393 {
394   std::shared_ptr<GeomAPI_Lin2d> aLine;
395   if (theFeature->getKind() == SketchPlugin_Line::ID()) {
396     std::shared_ptr<GeomAPI_Pnt2d> aStart =
397         toPoint( theFeature->attribute(SketchPlugin_Line::START_ID()) );
398     std::shared_ptr<GeomAPI_Pnt2d> aEnd =
399         toPoint( theFeature->attribute(SketchPlugin_Line::END_ID()) );
400     aLine = std::shared_ptr<GeomAPI_Lin2d>(new GeomAPI_Lin2d(aStart, aEnd));
401   }
402   return aLine;
403 }
404
405 static std::shared_ptr<GeomAPI_Circ2d> toCircle(const FeaturePtr& theFeature)
406 {
407   std::shared_ptr<GeomAPI_Circ2d> aCircle;
408   if (theFeature->getKind() == SketchPlugin_Arc::ID()) {
409     std::shared_ptr<GeomAPI_Pnt2d> aCenter =
410         toPoint( theFeature->attribute(SketchPlugin_Arc::CENTER_ID()) );
411     std::shared_ptr<GeomAPI_Pnt2d> aStart =
412         toPoint( theFeature->attribute(SketchPlugin_Arc::START_ID()) );
413     aCircle = std::shared_ptr<GeomAPI_Circ2d>(new GeomAPI_Circ2d(aCenter, aStart));
414   }
415   return aCircle;
416 }
417
418
419 void calculateFilletCenter(FeaturePtr theFilletFeatures[2],
420                            double theFilletRadius,
421                            const std::shared_ptr<GeomAPI_Ax3>& theSketchPlane,
422                            std::shared_ptr<GeomAPI_XY>& theCenter,
423                            std::shared_ptr<GeomAPI_XY>& theTangentA,
424                            std::shared_ptr<GeomAPI_XY>& theTangentB)
425 {
426   GeomShapePtr aShapeA = theFilletFeatures[0]->lastResult()->shape();
427   GeomShapePtr aShapeB = theFilletFeatures[1]->lastResult()->shape();
428
429   GeomAlgoAPI_Circ2dBuilder aCircBuilder(theSketchPlane);
430   aCircBuilder.addTangentCurve(aShapeA);
431   aCircBuilder.addTangentCurve(aShapeB);
432   aCircBuilder.setRadius(theFilletRadius);
433
434   std::shared_ptr<GeomAPI_Circ2d> aFilletCircle = aCircBuilder.circle();
435   if (!aFilletCircle)
436     return;
437
438   theCenter = aFilletCircle->center()->xy();
439   // tangent points
440   std::shared_ptr<GeomAPI_Pnt2d> aTgPoints[2];
441   for (int i = 0; i < 2; ++i) {
442     std::shared_ptr<GeomAPI_Circ2d> aCircle = toCircle(theFilletFeatures[i]);
443     if (aCircle)
444       aTgPoints[i] = aCircle->project(aFilletCircle->center());
445     else {
446       std::shared_ptr<GeomAPI_Lin2d> aLine = toLine(theFilletFeatures[i]);
447       if (aLine)
448         aTgPoints[i] = aLine->project(aFilletCircle->center());
449     }
450   }
451   theTangentA = aTgPoints[0]->xy();
452   theTangentB = aTgPoints[1]->xy();
453 }
454
455 double calculateFilletRadius(FeaturePtr theFilletFeatures[2])
456 {
457   double aLengths[2] = { 0, 0 };
458   for (int i = 0; i < 2; ++i) {
459     GeomShapePtr aShape = theFilletFeatures[i]->lastResult()->shape();
460     std::shared_ptr<GeomAPI_Edge> anEdge = std::dynamic_pointer_cast<GeomAPI_Edge>(aShape);
461     if (anEdge)
462       aLengths[i] = anEdge->length();
463   }
464   return std::min(aLengths[0], aLengths[1]) / 6.0;
465 }
466
467 std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
468 {
469   std::set<FeaturePtr> aCoincides;
470
471   std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt =
472     SketchPlugin_Tools::getCoincidencePoint(theConstraintCoincidence);
473
474   SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
475                                        SketchPlugin_ConstraintCoincidence::ENTITY_A(),
476                                        aCoincides);
477   SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
478                                        SketchPlugin_ConstraintCoincidence::ENTITY_B(),
479                                        aCoincides);
480
481   // Remove points from set of coincides.
482   std::set<FeaturePtr> aNewSetOfCoincides;
483   for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
484     std::shared_ptr<SketchPlugin_SketchEntity> aSketchEntity =
485       std::dynamic_pointer_cast<SketchPlugin_SketchEntity>(*anIt);
486     if(aSketchEntity.get() && aSketchEntity->isCopy()) {
487       continue;
488     }
489     if((*anIt)->getKind() == SketchPlugin_Line::ID()) {
490       aNewSetOfCoincides.insert(*anIt);
491     } else if((*anIt)->getKind() == SketchPlugin_Arc::ID()) {
492       AttributePtr anAttrCenter = (*anIt)->attribute(SketchPlugin_Arc::CENTER_ID());
493       std::shared_ptr<GeomDataAPI_Point2D> aPointCenter2D =
494         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrCenter);
495       if(aPointCenter2D.get() && aFilletPnt->isEqual(aPointCenter2D->pnt())) {
496         continue;
497       }
498       aNewSetOfCoincides.insert(*anIt);
499     }
500   }
501   aCoincides = aNewSetOfCoincides;
502
503   // If we still have more than two coincides remove auxilary entities from set of coincides.
504   if(aCoincides.size() > 2) {
505     aNewSetOfCoincides.clear();
506     for(std::set<FeaturePtr>::iterator
507         anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
508       if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
509         aNewSetOfCoincides.insert(*anIt);
510       }
511     }
512     aCoincides = aNewSetOfCoincides;
513   }
514
515   return aCoincides;
516 }
517
518 std::set<FeaturePtr> findFeaturesToRemove(const FeaturePtr theFeature,
519                                           const AttributePtr theAttribute) {
520   std::set<FeaturePtr> aFeaturesToBeRemoved;
521   std::set<AttributePtr> aRefs = theFeature->data()->refsToMe();
522   std::list<ResultPtr> aResults = theFeature->results();
523   for(std::list<ResultPtr>::const_iterator aResultsIt = aResults.cbegin();
524       aResultsIt != aResults.cend();
525       ++aResultsIt) {
526     ResultPtr aResult = *aResultsIt;
527     std::set<AttributePtr> aResultRefs = aResult->data()->refsToMe();
528     aRefs.insert(aResultRefs.begin(), aResultRefs.end());
529   }
530   for(std::set<AttributePtr>::const_iterator anIt = aRefs.cbegin();
531     anIt != aRefs.cend();
532     ++anIt) {
533     std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
534     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
535     if(aFeature->getKind() == SketchPlugin_Fillet::ID()) {
536       continue;
537     }
538     if(aFeature->getKind() == SketchPlugin_ConstraintLength::ID()
539         || aFeature->getKind() == SketchPlugin_ConstraintEqual::ID()
540         || aFeature->getKind() == SketchPlugin_ConstraintMiddle::ID()) {
541       aFeaturesToBeRemoved.insert(aFeature);
542     } else {
543       std::list<AttributePtr> anAttrs =
544           aFeature->data()->attributes(ModelAPI_AttributeRefAttr::typeId());
545       for(std::list<AttributePtr>::const_iterator aRefAttrsIt = anAttrs.cbegin();
546           aRefAttrsIt != anAttrs.cend();
547           ++aRefAttrsIt) {
548         AttributeRefAttrPtr anAttrRefAttr =
549           std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*aRefAttrsIt);
550         if(anAttrRefAttr.get() && anAttrRefAttr->attr() == theAttribute) {
551           aFeaturesToBeRemoved.insert(aFeature);
552         }
553       }
554     }
555   }
556   return aFeaturesToBeRemoved;
557 }