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