]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp
Salome HOME
Update all arc attributes on execute() (issue #1339)
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintFillet.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:    SketchPlugin_ConstraintFillet.cpp
4 // Created: 19 Mar 2015
5 // Author:  Artem ZHIDKOV
6
7 #include "SketchPlugin_ConstraintFillet.h"
8
9 #include <GeomAPI_Circ2d.h>
10 #include <GeomAPI_Dir2d.h>
11 #include <GeomAPI_Lin2d.h>
12 #include <GeomAPI_Pnt2d.h>
13 #include <GeomAPI_XY.h>
14 #include <GeomDataAPI_Point2D.h>
15 #include <ModelAPI_AttributeDouble.h>
16 #include <ModelAPI_AttributeRefList.h>
17 #include <ModelAPI_AttributeRefAttrList.h>
18 #include <ModelAPI_Data.h>
19 #include <ModelAPI_Events.h>
20 #include <ModelAPI_Session.h>
21 #include <ModelAPI_Validator.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_ConstraintCoincidence.h>
28 #include <SketchPlugin_ConstraintTangent.h>
29 #include <SketchPlugin_ConstraintRadius.h>
30 #include <SketchPlugin_Tools.h>
31
32 #include <Events_Loop.h>
33
34 #include <math.h>
35
36 const double tolerance = 1.e-7;
37 const double paramTolerance = 1.e-4;
38
39 /// \brief Attract specified point on theNewArc to the attribute of theFeature
40 static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
41   FeaturePtr theFeature, const std::string& theFeatureAttribute);
42
43 /// \brief Calculates center of fillet arc and coordinates of tangency points
44 static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
45                                   double theRadius, bool theNotInversed[2],
46                                   std::shared_ptr<GeomAPI_XY>& theCenter,
47                                   std::shared_ptr<GeomAPI_XY>& theTangentA,
48                                   std::shared_ptr<GeomAPI_XY>& theTangentB);
49
50 /// Get point on 1/3 length of edge from fillet point
51 static void getPointOnEdge(const FeaturePtr theFeature,
52                            const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
53                            std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
54
55 /// Get distance from point to feature
56 static double getProjectionDistance(const FeaturePtr theFeature,
57                              const std::shared_ptr<GeomAPI_Pnt2d> thePoint);
58
59 /// Get coincide edges for fillet
60 static std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence);
61
62 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
63 : myListOfPointsChangedInCode(false),
64   myRadiusChangedByUser(false),
65   myRadiusChangedInCode(false),
66   myRadiusInitialized(false)
67 {
68 }
69
70 void SketchPlugin_ConstraintFillet::initAttributes()
71 {
72   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeDouble::typeId());
73   data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttrList::typeId());
74 }
75
76 void SketchPlugin_ConstraintFillet::execute()
77 {
78   std::shared_ptr<ModelAPI_Data> aData = data();
79
80   // Check the base objects are initialized.
81   AttributeRefAttrListPtr aPointsRefList = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
82     aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
83   if(!aPointsRefList->isInitialized()) {
84     setError("Error: List of points is not initialized.");
85     return;
86   }
87
88   // Get fillet radius.
89   double aFilletRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
90     aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
91
92   // Wait all constraints being created, then send update events
93   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
94   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
95   if (isUpdateFlushed)
96     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
97
98   // Remove unused items.
99   for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
100       aPointsIter != myPointFeaturesMap.end();) {
101     if(myNewPoints.find(aPointsIter->first) == myNewPoints.end()) {
102       // Clear auxiliary flag on initial objects.
103       const FilletFeatures& aFilletFeatures = aPointsIter->second;
104       std::list<FeaturePtr>::const_iterator aFeatureIt;
105       for(aFeatureIt = aFilletFeatures.baseEdges.cbegin();
106           aFeatureIt != aFilletFeatures.baseEdges.cend();
107           ++aFeatureIt) {
108         (*aFeatureIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
109       }
110     }
111     ++aPointsIter;
112   }
113
114   DocumentPtr aDoc = sketch()->document();
115   for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
116       aPointsIter != myPointFeaturesMap.end();) {
117     if(myNewPoints.find(aPointsIter->first) != myNewPoints.end()) {
118       ++aPointsIter; // keep this point and result features.
119     } else {
120       // Remove all produced constraints.
121       const FilletFeatures& aFilletFeatures = aPointsIter->second;
122       std::list<FeaturePtr>::const_iterator aFeatureIt;
123       for(aFeatureIt = aFilletFeatures.resultConstraints.cbegin();
124           aFeatureIt != aFilletFeatures.resultConstraints.cend();
125           ++aFeatureIt) {
126         aDoc->removeFeature(*aFeatureIt);
127       }
128
129       // Remove all result edges.
130       for(aFeatureIt = aFilletFeatures.resultEdges.cbegin();
131           aFeatureIt != aFilletFeatures.resultEdges.cend();
132           ++aFeatureIt) {
133         aDoc->removeFeature(*aFeatureIt);
134       }
135
136       // Remove point from map.
137       myPointFeaturesMap.erase(aPointsIter++);
138     }
139   }
140
141   for(std::set<AttributePtr>::iterator aPointsIter = myNewPoints.begin();
142       aPointsIter != myNewPoints.end();
143       ++aPointsIter) {
144     AttributePtr aPointAttr = *aPointsIter;
145     std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2d = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aPointAttr);
146     if(!aFilletPoint2d.get()) {
147       setError("Error: One of the selected points is empty.");
148       return;
149     }
150     std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2d->pnt();
151
152     // Obtain base lines for fillet.
153     bool anIsNeedNewObjects = true;
154     FilletFeatures aFilletFeatures;
155     std::map<AttributePtr, FilletFeatures>::iterator aPrevPointsIter = myPointFeaturesMap.find(aPointAttr);
156     if(aPrevPointsIter != myPointFeaturesMap.end()) {
157       anIsNeedNewObjects = false;
158       aFilletFeatures = aPrevPointsIter->second;
159     }
160     FeaturePtr aBaseEdgeA, aBaseEdgeB;
161     if(!anIsNeedNewObjects) {
162       aBaseEdgeA = aFilletFeatures.baseEdges.front();
163       aBaseEdgeB = aFilletFeatures.baseEdges.back();
164     } else {
165       // Obtain constraint coincidence for the fillet point.
166       FeaturePtr aConstraintCoincidence;
167       const std::set<AttributePtr>& aRefsList = aFilletPoint2d->owner()->data()->refsToMe();
168       for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
169         std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
170         FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
171         if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
172           AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
173             aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
174           AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
175             aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
176           if(anAttrRefA.get() && !anAttrRefA->isObject()) {
177             AttributePtr anAttrA = anAttrRefA->attr();
178             if(aFilletPoint2d == anAttrA) {
179               aConstraintCoincidence = aConstrFeature;
180               break;
181             }
182           }
183           if(anAttrRefB.get() && !anAttrRefB->isObject()) {
184             AttributePtr anAttrB = anAttrRefB->attr();
185             if(aFilletPoint2d == anAttrB) {
186               aConstraintCoincidence = aConstrFeature;
187               break;
188             }
189           }
190         }
191       }
192
193       if(!aConstraintCoincidence.get()) {
194         setError("Error: No coincident edges at one of the selected points.");
195         return;
196       }
197
198       // Get coincide edges.
199       std::set<FeaturePtr> aCoincides = getCoincides(aConstraintCoincidence);
200       if(aCoincides.size() != 2) {
201         setError("Error: One of the selected points does not have two suitable edges for fillet.");
202         return;
203       }
204
205       std::set<FeaturePtr>::iterator aLinesIt = aCoincides.begin();
206       aBaseEdgeA = *aLinesIt++;
207       aBaseEdgeB = *aLinesIt;
208
209       aFilletFeatures.baseEdges.push_back(aBaseEdgeA);
210       aFilletFeatures.baseEdges.push_back(aBaseEdgeB);
211     }
212
213     if(!aBaseEdgeA.get() || !aBaseEdgeB.get()) {
214       setError("Error: One of the base edges is empty.");
215       return;
216     }
217
218     // Create new edges and arc if needed.
219     FeaturePtr aResultEdgeA, aResultEdgeB, aResultArc;
220     if(!anIsNeedNewObjects) {
221       // Obtain features from the list.
222       std::list<FeaturePtr>::iterator aResultEdgesIt = aFilletFeatures.resultEdges.begin();
223       aResultEdgeA = *aResultEdgesIt++;
224       aResultEdgeB = *aResultEdgesIt++;
225       aResultArc = *aResultEdgesIt;
226     } else {
227       // Copy edges and create arc.
228       aResultEdgeA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeA, sketch());
229       aResultEdgeB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(aBaseEdgeB, sketch());
230       aResultArc = sketch()->addFeature(SketchPlugin_Arc::ID());
231
232       aFilletFeatures.resultEdges.push_back(aResultEdgeA);
233       aFilletFeatures.resultEdges.push_back(aResultEdgeB);
234       aFilletFeatures.resultEdges.push_back(aResultArc);
235     }
236
237     // Calculate arc attributes
238     static const int aNbFeatures = 2;
239     FeaturePtr aBaseFeatures[aNbFeatures] = {aBaseEdgeA, aBaseEdgeB};
240     FeaturePtr aResultFeatures[aNbFeatures] = {aResultEdgeA, aResultEdgeB};
241     std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
242     bool isStart[aNbFeatures]; // indicates which point the features share
243     std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair -  to second
244     std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features
245     for (int i = 0; i < aNbFeatures; i++) {
246       std::string aStartAttr, aEndAttr;
247       if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
248         aStartAttr = SketchPlugin_Line::START_ID();
249         aEndAttr = SketchPlugin_Line::END_ID();
250       } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
251         aStartAttr = SketchPlugin_Arc::START_ID();
252         aEndAttr = SketchPlugin_Arc::END_ID();
253       } else { // wrong argument
254         setError("Error: One of the points has wrong coincide feature");
255         return;
256       }
257       aFeatAttributes[2*i] = aStartAttr;
258       aStartEndPnt[2*i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
259         aBaseFeatures[i]->attribute(aStartAttr))->pnt();
260       aFeatAttributes[2*i+1] = aEndAttr;
261       aStartEndPnt[2*i+1] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
262         aBaseFeatures[i]->attribute(aEndAttr))->pnt();
263     }
264     for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) {
265       for (int j = 0; j < 2; j++) // loop on start-end of each feature
266         if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPnt2d) < 1.e-10) {
267           isStart[aFeatInd] = (j==0);
268           break;
269         }
270     }
271     // tangent directions of the features
272     for (int i = 0; i < aNbFeatures; i++) {
273       std::shared_ptr<GeomAPI_XY> aDir;
274       if (aResultFeatures[i]->getKind() == SketchPlugin_Line::ID()) {
275         aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy());
276         if (!isStart[i])
277           aDir = aDir->multiplied(-1.0);
278       } else if (aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID()) {
279         std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
280           aResultFeatures[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
281         aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy();
282         aDir = aDir->decreased(aCenterPoint->xy());
283
284         double x = aDir->x();
285         double y = aDir->y();
286         aDir->setX(-y);
287         aDir->setY(x);
288         if (isStart[i] == std::dynamic_pointer_cast<SketchPlugin_Arc>(aBaseFeatures[i])->isReversed())
289           aDir = aDir->multiplied(-1.0);
290       }
291       aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
292     }
293
294     // By default, the start point of fillet arc is connected to FeatureA,
295     // and the end point - to FeatureB. But when the angle between TangentDirA and
296     // TangentDirB greater 180 degree, the sequaence of features need to be reversed.
297     double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis
298     bool isReversed = cosBA > 0.0;
299
300     // Calculate fillet arc parameters
301     std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
302     calculateFilletCenter(aBaseEdgeA, aBaseEdgeB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
303     if(!aCenter.get() || !aTangentPntA.get() || !aTangentPntB.get()) {
304       setError("Can not create fillet with the specified parameters.");
305       return;
306     }
307     // update features
308     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
309       aResultEdgeA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(aTangentPntA->x(), aTangentPntA->y());
310     aResultEdgeA->execute();
311     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
312       aResultEdgeB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue(aTangentPntB->x(), aTangentPntB->y());
313     aResultEdgeB->execute();
314     // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated"
315     // by arc; moreover, it may cause cyclicity in hte mechanism of updater
316     aResultArc->data()->blockSendAttributeUpdated(true);
317     std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
318       aResultArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCenter->x(), aCenter->y());
319     if(isReversed) {
320       std::shared_ptr<GeomAPI_XY> aTmp = aTangentPntA;
321       aTangentPntA = aTangentPntB;
322       aTangentPntB = aTmp;
323     }
324     std::shared_ptr<GeomDataAPI_Point2D> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
325       aResultArc->attribute(SketchPlugin_Arc::START_ID()));
326     std::shared_ptr<GeomDataAPI_Point2D> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
327       aResultArc->attribute(SketchPlugin_Arc::END_ID()));
328     if(aStartPoint->isInitialized() && aEndPoint->isInitialized() &&
329       (aStartPoint->pnt()->xy()->distance(aTangentPntA) > tolerance ||
330        aEndPoint->pnt()->xy()->distance(aTangentPntB) > tolerance)) {
331       std::dynamic_pointer_cast<SketchPlugin_Arc>(aResultArc)->setReversed(false);
332     }
333     aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y());
334     aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y());
335     aResultArc->data()->blockSendAttributeUpdated(false);
336     aResultArc->execute();
337
338     if(anIsNeedNewObjects) {
339       // Create list of additional constraints:
340       // 1. Coincidence of boundary points of features (copied lines/arcs) and fillet arc
341       // 1.1. coincidence
342       FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
343       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
344           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
345       aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::START_ID()));
346       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
347           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
348       int aFeatInd = isReversed ? 1 : 0;
349       int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1);
350       aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
351       recalculateAttributes(aResultArc, SketchPlugin_Arc::START_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
352       aConstraint->execute();
353       aFilletFeatures.resultConstraints.push_back(aConstraint);
354       ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
355       // 1.2. coincidence
356       aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
357       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
358           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
359       aRefAttr->setAttr(aResultArc->attribute(SketchPlugin_Arc::END_ID()));
360       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
361           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
362       aFeatInd = isReversed ? 0 : 1;
363       anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1);
364       aRefAttr->setAttr(aResultFeatures[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
365       recalculateAttributes(aResultArc, SketchPlugin_Arc::END_ID(), aResultFeatures[aFeatInd], aFeatAttributes[anAttrInd]);
366       aConstraint->execute();
367       aFilletFeatures.resultConstraints.push_back(aConstraint);
368       ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
369       // 2. Fillet arc radius
370       //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
371       //aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
372       //    aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
373       //aRefAttr->setObject(aNewArc->lastResult());
374       //std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
375       //    aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
376       //std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
377       //    aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
378       //    isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
379       //aConstraint->execute();
380       //myProducedFeatures.push_back(aConstraint);
381       //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
382       // 3. Tangency of fillet arc and features
383       for (int i = 0; i < aNbFeatures; i++) {
384         aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
385         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
386             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
387         aRefAttr->setObject(aResultArc->lastResult());
388         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
389             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
390         bool isArc = aResultFeatures[i]->getKind() == SketchPlugin_Arc::ID();
391         aRefAttr->setObject(isArc ? aResultFeatures[i]->lastResult() : aResultFeatures[i]->firstResult());
392         aConstraint->execute();
393         aFilletFeatures.resultConstraints.push_back(aConstraint);
394         ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
395       }
396       // 4. Coincidence of free boundaries of base and copied features
397       for (int i = 0; i < aNbFeatures; i++) {
398         anAttrInd = 2*i + (isStart[i] ? 1 : 0);
399         aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
400         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
401             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
402         aRefAttr->setAttr(aBaseFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
403         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
404             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
405         aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
406         aFilletFeatures.resultConstraints.push_back(aConstraint);
407       }
408       // 4.1. Additional tangency constraints when the fillet is based on arcs.
409       //      It is used to verify the created arc will be placed on a source.
410       for (int i = 0; i < aNbFeatures; ++i) {
411         if (aResultFeatures[i]->getKind() != SketchPlugin_Arc::ID())
412           continue;
413         aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
414         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
415             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
416         aRefAttr->setObject(aBaseFeatures[i]->lastResult());
417         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
418             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
419         aRefAttr->setObject(aResultFeatures[i]->lastResult());
420         aConstraint->execute();
421         aFilletFeatures.resultConstraints.push_back(aConstraint);
422         ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
423       }
424       // 5. Tangent points should be placed on the base features
425       for (int i = 0; i < aNbFeatures; i++) {
426         anAttrInd = 2*i + (isStart[i] ? 0 : 1);
427         aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
428         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
429             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
430         aRefAttr->setAttr(aResultFeatures[i]->attribute(aFeatAttributes[anAttrInd]));
431         aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
432             aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
433         aRefAttr->setObject(aBaseFeatures[i]->lastResult());
434         aFilletFeatures.resultConstraints.push_back(aConstraint);
435       }
436       // make base features auxiliary
437       aBaseEdgeA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
438       aBaseEdgeB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
439
440       // exchange the naming IDs of newly created and old line that become auxiliary
441       sketch()->exchangeIDs(aBaseEdgeA, aResultEdgeA);
442       sketch()->exchangeIDs(aBaseEdgeB, aResultEdgeB);
443
444       // store point and features in the map.
445       myPointFeaturesMap[aPointAttr] = aFilletFeatures;
446     } else {
447       // Update radius value
448       int aNbSubs = sketch()->numberOfSubs();
449       FeaturePtr aSubFeature;
450       for (int aSub = 0; aSub < aNbSubs; aSub++) {
451         aSubFeature = sketch()->subFeature(aSub);
452         if (aSubFeature->getKind() != SketchPlugin_ConstraintRadius::ID())
453           continue;
454         AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
455             aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
456         if (!aRefAttr || !aRefAttr->isObject())
457           continue;
458         FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
459         if (aFeature == aResultArc) {
460           AttributeDoublePtr aRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
461             aSubFeature->attribute(SketchPlugin_Constraint::VALUE()));
462           aRadius->setValue(aFilletRadius);
463           break;
464         }
465       }
466     }
467   }
468
469   // Send events to update the sub-features by the solver.
470   if(isUpdateFlushed) {
471     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
472   }
473 }
474
475 void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
476 {
477   if(theID == SketchPlugin_Constraint::ENTITY_A()) {
478     if(myListOfPointsChangedInCode) {
479       return;
480     }
481
482     // Clear list of new points.
483     myNewPoints.clear();
484
485     // Get list of points for fillets and current radius.
486     AttributeRefAttrListPtr aRefListOfFilletPoints = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(
487       data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
488     AttributeDoublePtr aRadiusAttribute = real(VALUE());
489     double aPrevRadius = aRadiusAttribute->value();
490     int aListSize = aRefListOfFilletPoints->size();
491     if(aListSize == 0) {
492       // If list is empty reset radius to zero (if it was not changed by user).
493       if(!myRadiusChangedByUser) {
494         myRadiusChangedInCode = true;
495         aRadiusAttribute->setValue(0);
496         myRadiusChangedInCode = false;
497       }
498
499       clearResults();
500
501       return;
502     }
503
504     // Iterate over points to get base lines an calculate radius for fillets.
505     double aMinimumRadius = 0;
506     std::list<std::pair<ObjectPtr, AttributePtr>> aSelectedPointsList = aRefListOfFilletPoints->list();
507     std::list<std::pair<ObjectPtr, AttributePtr>>::iterator anIter = aSelectedPointsList.begin();
508     std::set<AttributePtr> aPointsToSkeep;
509     for(int anIndex = 0; anIndex < aListSize; anIndex++, anIter++) {
510       AttributePtr aFilletPointAttr = (*anIter).second;
511       std::shared_ptr<GeomDataAPI_Point2D> aFilletPoint2D =
512         std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aFilletPointAttr);
513       if(!aFilletPoint2D.get()) {
514         myNewPoints.clear();
515         setError("Error: One of the selected points is invalid.");
516         return;
517       }
518
519       // If point was previously selected, skip it.
520       if(myPointFeaturesMap.find(aFilletPointAttr) != myPointFeaturesMap.end()) {
521         myNewPoints.insert(aFilletPointAttr);
522         aMinimumRadius = aPrevRadius;
523         continue;
524       }
525
526       // If point or coincident point is already in list remove it from attribute.
527       if(aPointsToSkeep.find(aFilletPointAttr) != aPointsToSkeep.end()) {
528         myListOfPointsChangedInCode = true;
529         aRefListOfFilletPoints->remove(aFilletPointAttr);
530         myListOfPointsChangedInCode = false;
531         continue;
532       }
533
534       // Obtain constraint coincidence for the fillet point.
535       FeaturePtr aConstraintCoincidence;
536       const std::set<AttributePtr>& aRefsList = aFilletPointAttr->owner()->data()->refsToMe();
537       for(std::set<AttributePtr>::const_iterator anIt = aRefsList.cbegin(); anIt != aRefsList.cend(); ++anIt) {
538         std::shared_ptr<ModelAPI_Attribute> anAttr = (*anIt);
539         FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
540         if(aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
541           AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
542             aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
543           AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
544             aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
545           if(anAttrRefA.get()) {
546             AttributePtr anAttrA = anAttrRefA->attr();
547             if(aFilletPointAttr == anAttrA) {
548               aConstraintCoincidence = aConstrFeature;
549               break;
550             }
551           }
552           if(anAttrRefB.get()) {
553             AttributePtr anAttrB = anAttrRefB->attr();
554             if(aFilletPointAttr == anAttrB) {
555               aConstraintCoincidence = aConstrFeature;
556               break;
557             }
558           }
559         }
560       }
561
562       if(!aConstraintCoincidence.get()) {
563         myNewPoints.clear();
564         setError("Error: No coincident edges at one of the selected points.");
565         return;
566       }
567
568       // Get coincides from constraint.
569       std::set<FeaturePtr> aCoincides;
570
571
572       SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
573                                            SketchPlugin_ConstraintCoincidence::ENTITY_A(),
574                                            aCoincides);
575       SketchPlugin_Tools::findCoincidences(aConstraintCoincidence,
576                                            SketchPlugin_ConstraintCoincidence::ENTITY_B(),
577                                            aCoincides);
578
579       // Remove points from set of coincides. Also get all attributes which is equal to this point to exclude it.
580       std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
581       std::set<FeaturePtr> aNewSetOfCoincides;
582       for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
583         std::string aFeatureKind = (*anIt)->getKind();
584         if(aFeatureKind == SketchPlugin_Point::ID()) {
585           AttributePtr anAttr = (*anIt)->attribute(SketchPlugin_Point::COORD_ID());
586           std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
587             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttr);
588           if(aPoint2D.get() && aFilletPnt2d->isEqual(aPoint2D->pnt())) {
589             aPointsToSkeep.insert(anAttr);
590           }
591         } else if(aFeatureKind == SketchPlugin_Line::ID()) {
592           AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Line::START_ID());
593           std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
594             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
595           if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
596             aPointsToSkeep.insert(anAttrStart);
597           }
598           AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Line::END_ID());
599           std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
600             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
601           if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
602             aPointsToSkeep.insert(anAttrEnd);
603           }
604           aNewSetOfCoincides.insert(*anIt);
605         } else if(aFeatureKind == SketchPlugin_Arc::ID() ) {
606           AttributePtr anAttrStart = (*anIt)->attribute(SketchPlugin_Arc::START_ID());
607           std::shared_ptr<GeomDataAPI_Point2D> aPointStart2D =
608             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrStart);
609           if(aPointStart2D.get() && aFilletPnt2d->isEqual(aPointStart2D->pnt())) {
610             aPointsToSkeep.insert(anAttrStart);
611           }
612           AttributePtr anAttrEnd = (*anIt)->attribute(SketchPlugin_Arc::END_ID());
613           std::shared_ptr<GeomDataAPI_Point2D> aPointEnd2D =
614             std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrEnd);
615           if(aPointEnd2D.get() && aFilletPnt2d->isEqual(aPointEnd2D->pnt())) {
616             aPointsToSkeep.insert(anAttrEnd);
617           }
618           aNewSetOfCoincides.insert(*anIt);
619         }
620       }
621       aCoincides = aNewSetOfCoincides;
622
623       // If we still have more than two coincides remove auxilary entities from set of coincides.
624       if(aCoincides.size() > 2) {
625         aNewSetOfCoincides.clear();
626         for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
627           if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
628             aNewSetOfCoincides.insert(*anIt);
629           }
630         }
631         aCoincides = aNewSetOfCoincides;
632       }
633
634       if(aCoincides.size() != 2) {
635         myNewPoints.clear();
636         setError("Error: One of the selected points does not have two suitable edges for fillet.");
637         return;
638       }
639
640       // Store base point for fillet.
641       aPointsToSkeep.insert(aFilletPointAttr);
642       myNewPoints.insert(aFilletPointAttr);
643
644       // Get base lines for fillet.
645       FeaturePtr anOldFeatureA, anOldFeatureB;
646       std::set<FeaturePtr>::iterator aLinesIt = aCoincides.begin();
647       anOldFeatureA = *aLinesIt++;
648       anOldFeatureB = *aLinesIt;
649
650       // Getting radius value if it was not changed by user.
651       if(!myRadiusChangedByUser) {
652         // Getting points located at 1/3 of edge length from fillet point.
653         std::shared_ptr<GeomAPI_Pnt2d> aFilletPnt2d = aFilletPoint2D->pnt();
654         std::shared_ptr<GeomAPI_Pnt2d> aPntA, aPntB;
655         getPointOnEdge(anOldFeatureA, aFilletPnt2d, aPntA);
656         getPointOnEdge(anOldFeatureB, aFilletPnt2d, aPntB);
657
658         /// Getting distances.
659         double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA);
660         double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB);
661         double aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0;
662         aMinimumRadius = aMinimumRadius == 0 ? aRadius : aRadius < aMinimumRadius ? aRadius : aMinimumRadius;
663       }
664     }
665
666     if(abs(aPrevRadius - aMinimumRadius) > tolerance) {
667       clearResults(); // if radius changed clear all results;
668     }
669
670     // Set new default radius if it was not changed by user.
671     if(!myRadiusChangedByUser) {
672       myRadiusChangedInCode = true;
673       aRadiusAttribute->setValue(aMinimumRadius);
674       myRadiusChangedInCode = false;
675     }
676
677   } else if(theID == SketchPlugin_Constraint::VALUE()) {
678     if(myRadiusInitialized && !myRadiusChangedInCode) {
679       myRadiusChangedByUser = true;
680     }
681     if(!myRadiusInitialized) {
682       myRadiusInitialized = true;
683     }
684   }
685 }
686
687 AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious)
688 {
689   if (!sketch())
690     return thePrevious;
691
692   AISObjectPtr anAIS = thePrevious;
693   /// TODO: Equal constraint presentation should be put here
694   return anAIS;
695 }
696
697 bool SketchPlugin_ConstraintFillet::isMacro() const
698 {
699   return true;
700 }
701
702 void SketchPlugin_ConstraintFillet::clearResults()
703 {
704   // Clear auxiliary flag on initial objects.
705   for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
706       aPointsIter != myPointFeaturesMap.end();) {
707     const FilletFeatures& aFilletFeatures = aPointsIter->second;
708     std::list<FeaturePtr>::const_iterator aFeatureIt;
709     for(aFeatureIt = aFilletFeatures.baseEdges.cbegin();
710         aFeatureIt != aFilletFeatures.baseEdges.cend();
711         ++aFeatureIt) {
712       (*aFeatureIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
713     }
714     ++aPointsIter;
715   }
716
717   // And remove all produced features.
718   DocumentPtr aDoc = sketch()->document();
719   for(std::map<AttributePtr, FilletFeatures>::iterator aPointsIter = myPointFeaturesMap.begin();
720       aPointsIter != myPointFeaturesMap.end();) {
721     // Remove all produced constraints.
722     const FilletFeatures& aFilletFeatures = aPointsIter->second;
723     std::list<FeaturePtr>::const_iterator aFeatureIt;
724     for(aFeatureIt = aFilletFeatures.resultConstraints.cbegin();
725         aFeatureIt != aFilletFeatures.resultConstraints.cend();
726         ++aFeatureIt) {
727       aDoc->removeFeature(*aFeatureIt);
728     }
729
730     // Remove all result edges.
731     for(aFeatureIt = aFilletFeatures.resultEdges.cbegin();
732         aFeatureIt != aFilletFeatures.resultEdges.cend();
733         ++aFeatureIt) {
734       aDoc->removeFeature(*aFeatureIt);
735     }
736
737     // Remove point from map.
738     myPointFeaturesMap.erase(aPointsIter++);
739   }
740 };
741
742
743 // =========   Auxiliary functions   =================
744 void recalculateAttributes(FeaturePtr theNewArc,  const std::string& theNewArcAttribute,
745                            FeaturePtr theFeature, const std::string& theFeatureAttribute)
746 {
747   std::shared_ptr<GeomAPI_Pnt2d> anArcPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
748       theNewArc->attribute(theNewArcAttribute))->pnt();
749   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
750       theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
751 }
752
753 /// \brief Find intersections of lines shifted along normal direction
754 void possibleFilletCenterLineLine(
755     std::shared_ptr<GeomAPI_XY> thePointA, std::shared_ptr<GeomAPI_Dir2d> theDirA,
756     std::shared_ptr<GeomAPI_XY> thePointB, std::shared_ptr<GeomAPI_Dir2d> theDirB,
757     double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
758 {
759   std::shared_ptr<GeomAPI_Dir2d> aDirAT(new GeomAPI_Dir2d(-theDirA->y(), theDirA->x()));
760   std::shared_ptr<GeomAPI_Dir2d> aDirBT(new GeomAPI_Dir2d(-theDirB->y(), theDirB->x()));
761   std::shared_ptr<GeomAPI_XY> aPntA, aPntB;
762   double aDet = theDirA->cross(theDirB);
763   for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
764     aPntA = thePointA->added(aDirAT->xy()->multiplied(aStepA * theRadius));
765     for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
766       aPntB = thePointB->added(aDirBT->xy()->multiplied(aStepB * theRadius));
767       double aVX = aDirAT->xy()->dot(aPntA);
768       double aVY = aDirBT->xy()->dot(aPntB);
769       std::shared_ptr<GeomAPI_XY> aPoint(new GeomAPI_XY(
770           (theDirB->x() * aVX - theDirA->x() * aVY) / aDet,
771           (theDirB->y() * aVX - theDirA->y() * aVY) / aDet));
772       theCenters.push_back(aPoint);
773     }
774   }
775 }
776
777 /// \brief Find intersections of line shifted along normal direction in both sides
778 ///        and a circle with extended radius
779 void possibleFilletCenterLineArc(
780     std::shared_ptr<GeomAPI_XY> theStartLine, std::shared_ptr<GeomAPI_Dir2d> theDirLine,
781     std::shared_ptr<GeomAPI_XY> theCenterArc, double theRadiusArc,
782     double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
783 {
784   std::shared_ptr<GeomAPI_Dir2d> aDirT(new GeomAPI_Dir2d(-theDirLine->y(), theDirLine->x()));
785   std::shared_ptr<GeomAPI_XY> aPnt;
786   double aDirNorm2 = theDirLine->dot(theDirLine);
787   double aRad = 0.0;
788   double aDirX = theDirLine->x();
789   double aDirX2 = theDirLine->x() * theDirLine->x();
790   double aDirY2 = theDirLine->y() * theDirLine->y();
791   double aDirXY = theDirLine->x() * theDirLine->y();
792   for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
793     aPnt = theStartLine->added(aDirT->xy()->multiplied(aStepA * theRadius));
794     double aCoeff = aDirT->xy()->dot(aPnt->decreased(theCenterArc));
795     double aCoeff2 = aCoeff * aCoeff;
796     for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
797       aRad = theRadiusArc + aStepB * theRadius;
798       double aD = aRad * aRad * aDirNorm2 - aCoeff2;
799       if (aD < 0.0)
800         continue;
801       double aDs = sqrt(aD);
802       double x1 = theCenterArc->x() + (aCoeff * aDirT->x() - aDirT->y() * aDs) / aDirNorm2;
803       double x2 = theCenterArc->x() + (aCoeff * aDirT->x() + aDirT->y() * aDs) / aDirNorm2;
804       double y1 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
805           aDirXY * (aPnt->x() - theCenterArc->x()) - theDirLine->y() * aDs) / aDirNorm2;
806       double y2 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
807           aDirXY * (aPnt->x() - theCenterArc->x()) + theDirLine->y() * aDs) / aDirNorm2;
808
809       std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
810       theCenters.push_back(aPoint1);
811       std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
812       theCenters.push_back(aPoint2);
813     }
814   }
815 }
816
817 /// \brief Find intersections of two circles with extended radii
818 void possibleFilletCenterArcArc(
819     std::shared_ptr<GeomAPI_XY> theCenterA, double theRadiusA,
820     std::shared_ptr<GeomAPI_XY> theCenterB, double theRadiusB,
821     double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
822 {
823   std::shared_ptr<GeomAPI_XY> aCenterDir = theCenterB->decreased(theCenterA);
824   double aCenterDist2 = aCenterDir->dot(aCenterDir);
825   double aCenterDist = sqrt(aCenterDist2);
826
827   double aRadA, aRadB;
828   for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
829     aRadA = theRadiusA + aStepA * theRadius;
830     for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
831       aRadB = theRadiusB + aStepB * theRadius;
832       if (aRadA + aRadB < aCenterDist || fabs(aRadA - aRadB) > aCenterDist)
833         continue; // there is no intersections
834
835       double aMedDist = (aRadA * aRadA - aRadB * aRadB + aCenterDist2) / (2.0 * aCenterDist);
836       double aHeight = sqrt(aRadA * aRadA - aMedDist * aMedDist);
837
838       double x1 = theCenterA->x() + (aMedDist * aCenterDir->x() + aCenterDir->y() * aHeight) / aCenterDist;
839       double y1 = theCenterA->y() + (aMedDist * aCenterDir->y() - aCenterDir->x() * aHeight) / aCenterDist;
840
841       double x2 = theCenterA->x() + (aMedDist * aCenterDir->x() - aCenterDir->y() * aHeight) / aCenterDist;
842       double y2 = theCenterA->y() + (aMedDist * aCenterDir->y() + aCenterDir->x() * aHeight) / aCenterDist;
843
844       std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
845       theCenters.push_back(aPoint1);
846       std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
847       theCenters.push_back(aPoint2);
848     }
849   }
850 }
851
852 void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
853                            double theRadius, bool theNotInversed[2],
854                            std::shared_ptr<GeomAPI_XY>& theCenter,
855                            std::shared_ptr<GeomAPI_XY>& theTangentA,
856                            std::shared_ptr<GeomAPI_XY>& theTangentB)
857 {
858   static const int aNbFeatures = 2;
859   FeaturePtr aFeature[aNbFeatures] = {theFeatureA, theFeatureB};
860   std::shared_ptr<GeomAPI_XY> aStart[aNbFeatures], aEnd[aNbFeatures], aCenter[aNbFeatures];
861   std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, aEndPoint;
862
863   for (int i = 0; i < aNbFeatures; i++) {
864     if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
865       aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
866           aFeature[i]->attribute(SketchPlugin_Line::START_ID()));
867       aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
868           aFeature[i]->attribute(SketchPlugin_Line::END_ID()));
869     } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
870       aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
871           aFeature[i]->attribute(SketchPlugin_Arc::START_ID()));
872       aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
873           aFeature[i]->attribute(SketchPlugin_Arc::END_ID()));
874       aCenter[i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
875           aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt()->xy();
876     } else
877       return;
878     aStart[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
879         new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()) :
880         new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()));
881     aEnd[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
882         new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()) :
883         new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()));
884   }
885
886   if (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
887       theFeatureB->getKind() == SketchPlugin_Line::ID()) {
888     std::shared_ptr<GeomAPI_Dir2d> aDir[2];
889     std::shared_ptr<GeomAPI_Dir2d> aDirT[2];
890     for (int i = 0; i < aNbFeatures; i++) {
891       aDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aEnd[i]->decreased(aStart[i])));
892       aDirT[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(-aDir[i]->y(), aDir[i]->x()));
893     }
894
895     // get and filter possible centers
896     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
897     possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1], theRadius, aSuspectCenters);
898     double aDot = 0.0;
899     std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
900     for (; anIt != aSuspectCenters.end(); anIt++) {
901       aDot = aDirT[0]->xy()->dot(aStart[0]->decreased(*anIt));
902       theTangentA = (*anIt)->added(aDirT[0]->xy()->multiplied(aDot));
903       if (theTangentA->decreased(aStart[0])->dot(aDir[0]->xy()) < 0.0)
904         continue; // incorrect position
905       aDot = aDirT[1]->xy()->dot(aStart[1]->decreased(*anIt));
906       theTangentB = (*anIt)->added(aDirT[1]->xy()->multiplied(aDot));
907       if (theTangentB->decreased(aStart[1])->dot(aDir[1]->xy()) < 0.0)
908         continue; // incorrect position
909       // the center is found, stop searching
910       theCenter = *anIt;
911       return;
912     }
913   } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
914       theFeatureB->getKind() == SketchPlugin_Line::ID()) || 
915       (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
916       theFeatureB->getKind() == SketchPlugin_Arc::ID())) {
917     int aLineInd = theFeatureA->getKind() == SketchPlugin_Line::ID() ? 0 : 1;
918     double anArcRadius = aStart[1-aLineInd]->distance(aCenter[1-aLineInd]);
919     std::shared_ptr<GeomAPI_Dir2d> aDirLine = std::shared_ptr<GeomAPI_Dir2d>(
920         new GeomAPI_Dir2d(aEnd[aLineInd]->decreased(aStart[aLineInd])));
921     std::shared_ptr<GeomAPI_Dir2d> aDirT = std::shared_ptr<GeomAPI_Dir2d>(
922         new GeomAPI_Dir2d(-aDirLine->y(), aDirLine->x()));
923
924     std::shared_ptr<GeomAPI_Dir2d> aStartArcDir = std::shared_ptr<GeomAPI_Dir2d>(
925         new GeomAPI_Dir2d(aStart[1-aLineInd]->decreased(aCenter[1-aLineInd])));
926     std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
927         new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd])));
928     double anArcAngle = aEndArcDir->angle(aStartArcDir);
929
930     // get possible centers and filter them
931     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
932     possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], anArcRadius, theRadius, aSuspectCenters);
933     double aDot = 0.0;
934     // the line is forward into the arc
935     double innerArc = aCenter[1-aLineInd]->decreased(aStart[aLineInd])->dot(aDirLine->xy());
936     std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
937     // The possible centers are ranged by their positions.
938     // If the point is not satisfy one of criteria, the weight is decreased with penalty.
939     int aBestWeight = 0;
940     std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
941     for (; anIt != aSuspectCenters.end(); anIt++) {
942       int aWeight = 2;
943       aDot = aDirT->xy()->dot(aStart[aLineInd]->decreased(*anIt));
944       aLineTgPoint = (*anIt)->added(aDirT->xy()->multiplied(aDot));
945       // Check the point is placed on the correct arc (penalty if false)
946       if (aCenter[1-aLineInd]->distance(*anIt) * innerArc > anArcRadius * innerArc)
947         aWeight -= 1;
948       std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
949           new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1-aLineInd])));
950       double aCurAngle = aCurDir->angle(aStartArcDir);
951       if (anArcAngle < 0.0) aCurAngle *= -1.0;
952       if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle))
953         continue;
954       if (aWeight > aBestWeight)
955         aBestWeight = aWeight;
956       else if (aWeight < aBestWeight ||
957                aStart[aLineInd]->distance(*anIt) >
958                aStart[aLineInd]->distance(theCenter)) // <-- take closer point
959         continue;
960       // the center is found, stop searching
961       theCenter = *anIt;
962       anArcTgPoint = aCenter[1-aLineInd]->added(aCurDir->xy()->multiplied(anArcRadius));
963       if (theFeatureA->getKind() == SketchPlugin_Line::ID()) {
964         theTangentA = aLineTgPoint;
965         theTangentB = anArcTgPoint;
966       } else {
967         theTangentA = anArcTgPoint;
968         theTangentB = aLineTgPoint;
969       }
970       //return;
971     }
972   } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
973       theFeatureB->getKind() == SketchPlugin_Arc::ID()) {
974     double anArcRadius[aNbFeatures];
975     double anArcAngle[aNbFeatures];
976     std::shared_ptr<GeomAPI_Dir2d> aStartArcDir[aNbFeatures];
977     for (int i = 0; i < aNbFeatures; i++) {
978       anArcRadius[i] = aStart[i]->distance(aCenter[i]);
979       aStartArcDir[i] = std::shared_ptr<GeomAPI_Dir2d>(
980           new GeomAPI_Dir2d(aStart[i]->decreased(aCenter[i])));
981       std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
982           new GeomAPI_Dir2d(aEnd[i]->decreased(aCenter[i])));
983       anArcAngle[i] = aEndArcDir->angle(aStartArcDir[i]);
984     }
985
986     // get and filter possible centers
987     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
988     possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1], anArcRadius[1], theRadius, aSuspectCenters);
989     double aDot = 0.0;
990     std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
991     std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
992     for (; anIt != aSuspectCenters.end(); anIt++) {
993       std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
994           new GeomAPI_Dir2d((*anIt)->decreased(aCenter[0])));
995       double aCurAngle = aCurDir->angle(aStartArcDir[0]);
996       if (anArcAngle[0] < 0.0) aCurAngle *= -1.0;
997       if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[0]))
998         continue; // incorrect position
999       theTangentA = aCenter[0]->added(aCurDir->xy()->multiplied(anArcRadius[0]));
1000
1001       aCurDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1])));
1002       aCurAngle = aCurDir->angle(aStartArcDir[1]);
1003       if (anArcAngle[1] < 0.0) aCurAngle *= -1.0;
1004       if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[1]))
1005         continue; // incorrect position
1006       theTangentB = aCenter[1]->added(aCurDir->xy()->multiplied(anArcRadius[1]));
1007
1008       // the center is found, stop searching
1009       theCenter = *anIt;
1010       return;
1011     }
1012   }
1013 }
1014
1015 void getPointOnEdge(const FeaturePtr theFeature,
1016                     const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
1017                     std::shared_ptr<GeomAPI_Pnt2d>& thePoint) {
1018   if(theFeature->getKind() == SketchPlugin_Line::ID()) {
1019     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1020       theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
1021     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1022       theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
1023     if(aPntStart->distance(theFilletPoint) > 1.e-7) {
1024       aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1025         theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
1026       aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1027         theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
1028     }
1029     thePoint.reset( new GeomAPI_Pnt2d(aPntStart->xy()->added( aPntEnd->xy()->decreased( aPntStart->xy() )->multiplied(1.0 / 3.0) ) ) );
1030   } else {
1031     std::shared_ptr<GeomAPI_Pnt2d> aPntTemp;
1032     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1033       theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
1034     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1035       theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
1036     if(theFeature->attribute(SketchPlugin_Arc::INVERSED_ID())) {
1037       aPntTemp = aPntStart;
1038       aPntStart = aPntEnd;
1039       aPntEnd = aPntTemp;
1040     }
1041     std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1042       theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
1043     std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
1044     double aStartParameter(0), anEndParameter(0);
1045     aCirc->parameter(aPntStart, paramTolerance, aStartParameter);
1046     aCirc->parameter(aPntEnd, paramTolerance, anEndParameter);
1047     if(aPntStart->distance(theFilletPoint) > tolerance) {
1048       double aTmpParameter = aStartParameter;
1049       aStartParameter = anEndParameter;
1050       anEndParameter = aTmpParameter;
1051     }
1052     double aPntParameter = aStartParameter + (anEndParameter - aStartParameter) / 3.0;
1053     aCirc->D0(aPntParameter, thePoint);
1054   }
1055 }
1056
1057 double getProjectionDistance(const FeaturePtr theFeature,
1058                              const std::shared_ptr<GeomAPI_Pnt2d> thePoint)
1059 {
1060   std::shared_ptr<GeomAPI_Pnt2d> aProjectPnt;
1061   if(theFeature->getKind() == SketchPlugin_Line::ID()) {
1062     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1063       theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
1064     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1065       theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
1066     std::shared_ptr<GeomAPI_Lin2d> aLin(new GeomAPI_Lin2d(aPntStart, aPntEnd));
1067     aProjectPnt = aLin->project(thePoint);
1068   } else {
1069     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1070       theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
1071     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1072       theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
1073     std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1074       theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
1075     std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
1076     aProjectPnt = aCirc->project(thePoint);
1077   }
1078   if(aProjectPnt.get()) {
1079     return aProjectPnt->distance(thePoint);
1080   }
1081   return -1;
1082 }
1083
1084 std::set<FeaturePtr> getCoincides(const FeaturePtr& theConstraintCoincidence)
1085 {
1086   std::set<FeaturePtr> aCoincides;
1087
1088   SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
1089                                        SketchPlugin_ConstraintCoincidence::ENTITY_A(),
1090                                        aCoincides);
1091   SketchPlugin_Tools::findCoincidences(theConstraintCoincidence,
1092                                        SketchPlugin_ConstraintCoincidence::ENTITY_B(),
1093                                        aCoincides);
1094
1095   // Remove points from set of coincides.
1096   std::set<FeaturePtr> aNewSetOfCoincides;
1097   for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
1098     if((*anIt)->getKind() == SketchPlugin_Line::ID() ||
1099         (*anIt)->getKind() == SketchPlugin_Arc::ID() ) {
1100       aNewSetOfCoincides.insert(*anIt);
1101     }
1102   }
1103   aCoincides = aNewSetOfCoincides;
1104
1105   // If we still have more than two coincides remove auxilary entities from set of coincides.
1106   if(aCoincides.size() > 2) {
1107     aNewSetOfCoincides.clear();
1108     for(std::set<FeaturePtr>::iterator anIt = aCoincides.begin(); anIt != aCoincides.end(); ++anIt) {
1109       if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
1110         aNewSetOfCoincides.insert(*anIt);
1111       }
1112     }
1113     aCoincides = aNewSetOfCoincides;
1114   }
1115
1116   return aCoincides;
1117 }