]> SALOME platform Git repositories - modules/shaper.git/blob - src/SketchPlugin/SketchPlugin_ConstraintFillet.cpp
Salome HOME
Fix for crash in fillet
[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_AttributeRefAttr.h>
17 #include <ModelAPI_AttributeRefList.h>
18 #include <ModelAPI_Data.h>
19 #include <ModelAPI_Events.h>
20 #include <ModelAPI_ResultConstruction.h>
21 #include <ModelAPI_Session.h>
22 #include <ModelAPI_Validator.h>
23
24 #include <SketchPlugin_Arc.h>
25 #include <SketchPlugin_Line.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 <SketcherPrs_Factory.h>
33 #include <SketcherPrs_Tools.h>
34
35 #include <Config_PropManager.h>
36 #include <Events_Loop.h>
37
38 #define _USE_MATH_DEFINES
39 #include <math.h>
40
41 static const std::string PREVIOUS_VALUE("FilletPreviousRadius");
42
43 const double tolerance = 1.e-7;
44 const double paramTolerance = 1.e-4;
45
46 /// \brief Attract specified point on theNewArc to the attribute of theFeature
47 static void recalculateAttributes(FeaturePtr theNewArc, const std::string& theNewArcAttribute,
48   FeaturePtr theFeature, const std::string& theFeatureAttribute);
49
50 /// \brief Calculates center of fillet arc and coordinates of tangency points
51 static void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
52                                   double theRadius, bool theNotInversed[2],
53                                   std::shared_ptr<GeomAPI_XY>& theCenter,
54                                   std::shared_ptr<GeomAPI_XY>& theTangentA,
55                                   std::shared_ptr<GeomAPI_XY>& theTangentB);
56
57 /// Get point on 1/3 length of edge from fillet point
58 static void getPointOnEdge(const FeaturePtr theFeature,
59                            const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
60                            std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
61
62 /// Get distance from point to feature
63 static double getProjectionDistance(const FeaturePtr theFeature,
64                              const std::shared_ptr<GeomAPI_Pnt2d> thePoint);
65
66 SketchPlugin_ConstraintFillet::SketchPlugin_ConstraintFillet()
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_AttributeRefAttr::typeId());
74   data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
75   // This attribute used to store base edges
76   data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeRefList::typeId());
77   data()->addAttribute(PREVIOUS_VALUE, ModelAPI_AttributeDouble::typeId());
78   // initialize attribute not applicable for user
79   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
80   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
81   data()->attribute(PREVIOUS_VALUE)->setInitialized();
82   std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(PREVIOUS_VALUE))->setValue(0.0);
83 }
84
85 void SketchPlugin_ConstraintFillet::execute()
86 {
87   static const double aTol = 1.e-7;
88
89   // the viewer update should be blocked in order to avoid the temporaty fillet sub-features visualization
90   // before they are processed by the solver
91   //std::shared_ptr<Events_Message> aMsg = std::shared_ptr<Events_Message>(
92   //    new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_BLOCKED)));
93   //Events_Loop::loop()->send(aMsg);
94
95   std::shared_ptr<ModelAPI_Data> aData = data();
96   ResultConstructionPtr aRC;
97   // Check the base objects are initialized
98   double aFilletRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
99       aData->attribute(SketchPlugin_Constraint::VALUE()))->value();
100   AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
101       aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
102   if (!aBaseA->isInitialized() || aBaseA->isObject()) {
103     setError("Bad vertex selected");
104     return;
105   }
106
107   // Obtain fillet point
108   std::shared_ptr<GeomDataAPI_Point2D> aBasePoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aBaseA->attr());
109   if (!aBasePoint) {
110     setError("Bad vertex selected");
111     return;
112   }
113   std::shared_ptr<GeomAPI_Pnt2d> aFilletPoint = aBasePoint->pnt();
114
115   // Check the fillet shapes is not initialized yet
116   AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
117       aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
118   bool needNewObjects = aRefListOfFillet->size() == 0;
119
120   AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
121       aData->attribute(SketchPlugin_Constraint::ENTITY_C()));
122
123   // Obtain base features
124   FeaturePtr anOldFeatureA, anOldFeatureB;
125   std::list<ObjectPtr> anOldFeatList = aRefListOfBaseLines->list();
126   std::list<ObjectPtr>::iterator aFeatIt = anOldFeatList.begin();
127   anOldFeatureA = ModelAPI_Feature::feature(*aFeatIt++);
128   anOldFeatureB = ModelAPI_Feature::feature(*aFeatIt);
129
130   if(!anOldFeatureA.get() || !anOldFeatureB.get()) {
131     setError("One of the edges is empty");
132     return;
133   }
134
135   FeaturePtr aNewFeatureA, aNewFeatureB, aNewArc;
136   if (needNewObjects) {
137     // Create list of objects composing a fillet
138     // copy aFeatureA
139     aNewFeatureA = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureA, sketch());
140     // copy aFeatureB
141     aNewFeatureB = SketchPlugin_Sketch::addUniqueNamedCopiedFeature(anOldFeatureB, sketch());
142     // create filleting arc (it will be attached to the list later)
143     aNewArc = sketch()->addFeature(SketchPlugin_Arc::ID());
144   } else {
145     // Obtain features from the list
146     std::list<ObjectPtr> aNewFeatList = aRefListOfFillet->list();
147     std::list<ObjectPtr>::iterator aFeatIt = aNewFeatList.begin();
148     aNewFeatureA = ModelAPI_Feature::feature(*aFeatIt++);
149     aNewFeatureB = ModelAPI_Feature::feature(*aFeatIt++);
150     aNewArc = ModelAPI_Feature::feature(*aFeatIt);
151   }
152
153   // Calculate arc attributes
154   static const int aNbFeatures = 2;
155   FeaturePtr aFeature[aNbFeatures] = {anOldFeatureA, anOldFeatureB};
156   FeaturePtr aNewFeature[aNbFeatures] = {aNewFeatureA, aNewFeatureB};
157   std::shared_ptr<GeomAPI_Dir2d> aTangentDir[aNbFeatures]; // tangent directions of the features in coincident point
158   bool isStart[aNbFeatures]; // indicates which point the features share
159   std::shared_ptr<GeomAPI_Pnt2d> aStartEndPnt[aNbFeatures * 2]; // first pair of points relate to first feature, second pair -  to second
160   std::string aFeatAttributes[aNbFeatures * 2]; // attributes of features
161   for (int i = 0; i < aNbFeatures; i++) {
162     std::string aStartAttr, aEndAttr;
163     if (aNewFeature[i]->getKind() == SketchPlugin_Line::ID()) {
164       aStartAttr = SketchPlugin_Line::START_ID();
165       aEndAttr = SketchPlugin_Line::END_ID();
166     } else if (aNewFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
167       aStartAttr = SketchPlugin_Arc::START_ID();
168       aEndAttr = SketchPlugin_Arc::END_ID();
169     } else { // wrong argument
170       aRefListOfFillet->remove(aNewFeatureA);
171       aRefListOfFillet->remove(aNewFeatureB);
172       aRefListOfFillet->remove(aNewArc);
173       aRefListOfBaseLines->clear();
174       return;
175     }
176     aFeatAttributes[2*i] = aStartAttr;
177     aStartEndPnt[2*i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
178         aFeature[i]->attribute(aStartAttr))->pnt();
179     aFeatAttributes[2*i+1] = aEndAttr;
180     aStartEndPnt[2*i+1] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
181         aFeature[i]->attribute(aEndAttr))->pnt();
182   }
183   for (int aFeatInd = 0; aFeatInd < aNbFeatures; aFeatInd++) {
184     for (int j = 0; j < 2; j++) // loop on start-end of each feature
185       if (aStartEndPnt[aFeatInd * aNbFeatures + j]->distance(aFilletPoint) < 1.e-10) {
186         isStart[aFeatInd] = (j==0);
187         break;
188       }
189   }
190   // tangent directions of the features
191   for (int i = 0; i < aNbFeatures; i++) {
192     std::shared_ptr<GeomAPI_XY> aDir;
193     if (aNewFeature[i]->getKind() == SketchPlugin_Line::ID()) {
194       aDir = aStartEndPnt[2*i+1]->xy()->decreased(aStartEndPnt[2*i]->xy());
195       if (!isStart[i])
196         aDir = aDir->multiplied(-1.0);
197     } else if (aNewFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
198       std::shared_ptr<GeomAPI_Pnt2d> aCenterPoint =
199           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
200           aNewFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
201       aDir = isStart[i] ? aStartEndPnt[2*i]->xy() : aStartEndPnt[2*i+1]->xy();
202       aDir = aDir->decreased(aCenterPoint->xy());
203
204       double x = aDir->x();
205       double y = aDir->y();
206       aDir->setX(-y);
207       aDir->setY(x);
208       if (isStart[i] == std::dynamic_pointer_cast<SketchPlugin_Arc>(aFeature[i])->isReversed())
209         aDir = aDir->multiplied(-1.0);
210     }
211     aTangentDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aDir));
212   }
213
214   // Wait all constraints being created, then send update events
215   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
216   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
217   if (isUpdateFlushed)
218     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
219
220   // By default, the start point of fillet arc is connected to FeatureA,
221   // and the end point - to FeatureB. But when the angle between TangentDirA and
222   // TangentDirB greater 180 degree, the sequaence of features need to be reversed.
223   double cosBA = aTangentDir[0]->cross(aTangentDir[1]); // cos(B-A), where A and B - angles between corresponding tanget direction and the X axis
224   bool isReversed = cosBA > 0.0;
225
226   // Calculate fillet arc parameters
227   std::shared_ptr<GeomAPI_XY> aCenter, aTangentPntA, aTangentPntB;
228   calculateFilletCenter(anOldFeatureA, anOldFeatureB, aFilletRadius, isStart, aCenter, aTangentPntA, aTangentPntB);
229   if(!aCenter.get() || !aTangentPntA.get() || !aTangentPntB.get()) {
230     setError("Can not create fillet with the specified parameters.");
231     return;
232   }
233   // update features
234   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
235       aNewFeatureA->attribute(aFeatAttributes[isStart[0] ? 0 : 1]))->setValue(
236       aTangentPntA->x(), aTangentPntA->y());
237   aNewFeatureA->execute();
238   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
239       aNewFeatureB->attribute(aFeatAttributes[2 + (isStart[1] ? 0 : 1)]))->setValue(
240       aTangentPntB->x(), aTangentPntB->y());
241   aNewFeatureB->execute();
242   // update fillet arc: make the arc correct for sure, so, it is not needed to process the "attribute updated"
243   // by arc; moreover, it may cause cyclicity in hte mechanism of updater
244   aNewArc->data()->blockSendAttributeUpdated(true);
245   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
246       aNewArc->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(
247       aCenter->x(), aCenter->y());
248   if (isReversed) {
249     std::shared_ptr<GeomAPI_XY> aTmp = aTangentPntA;
250     aTangentPntA = aTangentPntB;
251     aTangentPntB = aTmp;
252   }
253   std::shared_ptr<GeomDataAPI_Point2D> aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
254       aNewArc->attribute(SketchPlugin_Arc::START_ID()));
255   std::shared_ptr<GeomDataAPI_Point2D> aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
256       aNewArc->attribute(SketchPlugin_Arc::END_ID()));
257   if (aStartPoint->isInitialized() && aEndPoint->isInitialized() &&
258      (aStartPoint->pnt()->xy()->distance(aTangentPntA) > aTol ||
259       aEndPoint->pnt()->xy()->distance(aTangentPntB) > aTol))
260     std::dynamic_pointer_cast<SketchPlugin_Arc>(aNewArc)->setReversed(false);
261   aStartPoint->setValue(aTangentPntA->x(), aTangentPntA->y());
262   aEndPoint->setValue(aTangentPntB->x(), aTangentPntB->y());
263   aNewArc->data()->blockSendAttributeUpdated(false);
264   aNewArc->execute();
265
266   if (needNewObjects) {
267     // attach new arc to the list
268     aRefListOfFillet->append(aNewFeatureA->lastResult());
269     aRefListOfFillet->append(aNewFeatureB->lastResult());
270     aRefListOfFillet->append(aNewArc->lastResult());
271
272     myProducedFeatures.push_back(aNewFeatureA);
273     myProducedFeatures.push_back(aNewFeatureB);
274     myProducedFeatures.push_back(aNewArc);
275
276     // Create list of additional constraints:
277     // 1. Coincidence of boundary points of features (copied lines/arcs) and fillet arc
278     // 1.1. coincidence
279     FeaturePtr aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
280     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
281         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
282     aRefAttr->setAttr(aNewArc->attribute(SketchPlugin_Arc::START_ID()));
283     aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
284         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
285     int aFeatInd = isReversed ? 1 : 0;
286     int anAttrInd = (isReversed ? 2 : 0) + (isStart[isReversed ? 1 : 0] ? 0 : 1);
287     aRefAttr->setAttr(aNewFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
288     recalculateAttributes(aNewArc, SketchPlugin_Arc::START_ID(), aNewFeature[aFeatInd], aFeatAttributes[anAttrInd]);
289     aConstraint->execute();
290     myProducedFeatures.push_back(aConstraint);
291     ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
292     // 1.2. coincidence
293     aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
294     aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
295         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
296     aRefAttr->setAttr(aNewArc->attribute(SketchPlugin_Arc::END_ID()));
297     aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
298         aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
299     aFeatInd = isReversed ? 0 : 1;
300     anAttrInd = (isReversed ? 0 : 2) + (isStart[isReversed ? 0 : 1] ? 0 : 1);
301     aRefAttr->setAttr(aNewFeature[aFeatInd]->attribute(aFeatAttributes[anAttrInd]));
302     recalculateAttributes(aNewArc, SketchPlugin_Arc::END_ID(), aNewFeature[aFeatInd], aFeatAttributes[anAttrInd]);
303     aConstraint->execute();
304     myProducedFeatures.push_back(aConstraint);
305     ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
306     // 2. Fillet arc radius
307     //aConstraint = sketch()->addFeature(SketchPlugin_ConstraintRadius::ID());
308     //aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
309     //    aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
310     //aRefAttr->setObject(aNewArc->lastResult());
311     //std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
312     //    aConstraint->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aFilletRadius);
313     //std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
314     //    aConstraint->attribute(SketchPlugin_Constraint::FLYOUT_VALUE_PNT()))->setValue(
315     //    isStart[0] ? aStartEndPnt[0] : aStartEndPnt[1]);
316     //aConstraint->execute();
317     //myProducedFeatures.push_back(aConstraint);
318     //ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
319     // 3. Tangency of fillet arc and features
320     for (int i = 0; i < aNbFeatures; i++) {
321       aConstraint = sketch()->addFeature(SketchPlugin_ConstraintTangent::ID());
322       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
323           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
324       aRefAttr->setObject(aNewArc->lastResult());
325       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
326           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
327       bool isArc = aNewFeature[i]->getKind() == SketchPlugin_Arc::ID();
328       aRefAttr->setObject(isArc ? aNewFeature[i]->lastResult() : aNewFeature[i]->firstResult());
329       aConstraint->execute();
330       myProducedFeatures.push_back(aConstraint);
331       ModelAPI_EventCreator::get()->sendUpdated(aConstraint, anUpdateEvent);
332     }
333     // 4. Coincidence of free boundaries of base and copied features
334     for (int i = 0; i < aNbFeatures; i++) {
335       anAttrInd = 2*i + (isStart[i] ? 1 : 0);
336       aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
337       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
338           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
339       aRefAttr->setAttr(aFeature[i]->attribute(aFeatAttributes[anAttrInd]));
340       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
341           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
342       aRefAttr->setAttr(aNewFeature[i]->attribute(aFeatAttributes[anAttrInd]));
343       myProducedFeatures.push_back(aConstraint);
344     }
345     // 5. Tangent points should be placed on the base features
346     for (int i = 0; i < aNbFeatures; i++) {
347       anAttrInd = 2*i + (isStart[i] ? 0 : 1);
348       aConstraint = sketch()->addFeature(SketchPlugin_ConstraintCoincidence::ID());
349       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
350           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
351       aRefAttr->setAttr(aNewFeature[i]->attribute(aFeatAttributes[anAttrInd]));
352       aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
353           aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
354       aRefAttr->setObject(aFeature[i]->lastResult());
355       myProducedFeatures.push_back(aConstraint);
356     }
357     // make base features auxiliary
358     anOldFeatureA->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
359     anOldFeatureB->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(true);
360     myBaseObjects.clear();
361     myBaseObjects.push_back(anOldFeatureA);
362     myBaseObjects.push_back(anOldFeatureB);
363     // exchange the naming IDs of newly created and old line that become auxiliary
364     sketch()->exchangeIDs(anOldFeatureA, aNewFeatureA);
365     sketch()->exchangeIDs(anOldFeatureB, aNewFeatureB);
366   } else {
367     // Update radius value
368     int aNbSubs = sketch()->numberOfSubs();
369     FeaturePtr aSubFeature;
370     for (int aSub = 0; aSub < aNbSubs; aSub++) {
371       aSubFeature = sketch()->subFeature(aSub);
372       if (aSubFeature->getKind() != SketchPlugin_ConstraintRadius::ID())
373         continue;
374       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
375           aSubFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
376       if (!aRefAttr || !aRefAttr->isObject())
377         continue;
378       FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
379       if (aFeature == aNewArc) {
380         AttributeDoublePtr aRadius = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(
381           aSubFeature->attribute(SketchPlugin_Constraint::VALUE()));
382         aRadius->setValue(aFilletRadius);
383         break;
384       }
385     }
386   }
387
388   // send events to update the sub-features by the solver
389   if (isUpdateFlushed)
390     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
391
392   // the viewer update should be unblocked in order after the fillet features
393   // are processed by the solver
394   //aMsg = std::shared_ptr<Events_Message>(
395   //              new Events_Message(Events_Loop::eventByName(EVENT_UPDATE_VIEWER_UNBLOCKED)));
396   //Events_Loop::loop()->send(aMsg);
397 }
398
399 void SketchPlugin_ConstraintFillet::attributeChanged(const std::string& theID)
400 {
401   if (theID == SketchPlugin_Constraint::ENTITY_A()) {
402     // clear the list of fillet entities
403     AttributeRefListPtr aRefListOfFillet = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
404         data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
405     aRefListOfFillet->clear();
406
407     // clear the list of base features
408     AttributeRefListPtr aRefListOfBaseLines = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
409         data()->attribute(SketchPlugin_Constraint::ENTITY_C()));
410       aRefListOfBaseLines->clear();
411
412     // remove all produced objects and constraints
413     DocumentPtr aDoc = sketch()->document();
414     std::list<FeaturePtr>::iterator aCIt = myProducedFeatures.begin();
415     for (; aCIt != myProducedFeatures.end(); ++aCIt)
416       aDoc->removeFeature(*aCIt);
417     myProducedFeatures.clear();
418
419     // clear auxiliary flag on initial objects
420     for (aCIt = myBaseObjects.begin(); aCIt != myBaseObjects.end(); ++aCIt)
421       (*aCIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(false);
422     myBaseObjects.clear();
423
424     // Obtain fillet point
425     AttributeRefAttrPtr aBaseA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
426         data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
427     if(!aBaseA->isInitialized() || aBaseA->isObject()) {
428       return;
429     }
430     AttributePtr anAttrBaseA = aBaseA->attr();
431     std::shared_ptr<GeomDataAPI_Point2D> aBasePoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttrBaseA);
432     if (!aBasePoint) {
433       return;
434     }
435     std::shared_ptr<GeomAPI_Pnt2d> aFilletPoint = aBasePoint->pnt();
436
437     // Obtain conicident edges
438     const std::set<AttributePtr>& aRefsList = anAttrBaseA->owner()->data()->refsToMe();
439     std::set<AttributePtr>::const_iterator aIt;
440     FeaturePtr aCoincident;
441     for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
442       std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
443       FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
444       if (aConstrFeature->getKind() == SketchPlugin_ConstraintCoincidence::ID()) {
445         AttributeRefAttrPtr anAttrRefA = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
446           aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_A()));
447         AttributeRefAttrPtr anAttrRefB = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
448           aConstrFeature->attribute(SketchPlugin_ConstraintCoincidence::ENTITY_B()));
449         if(anAttrRefA.get() && !anAttrRefA->isObject()) {
450           AttributePtr anAttrA = anAttrRefA->attr();
451           if(anAttrBaseA == anAttrA) {
452             aCoincident = aConstrFeature;
453             break;
454           }
455         }
456         if(anAttrRefA.get() && !anAttrRefB->isObject()) {
457           AttributePtr anAttrB = anAttrRefB->attr();
458           if(anAttrBaseA == anAttrB) {
459             aCoincident = aConstrFeature;
460             break;
461           }
462         }
463       }
464     }
465
466     if(!aCoincident.get()) {
467       setError("No coincident edges at selected vertex");
468       return;
469     }
470
471     std::set<FeaturePtr> aCoinsideLines;
472     SketchPlugin_Tools::findCoincidences(aCoincident,
473                                          SketchPlugin_ConstraintCoincidence::ENTITY_A(),
474                                          aCoinsideLines);
475     SketchPlugin_Tools::findCoincidences(aCoincident,
476                                          SketchPlugin_ConstraintCoincidence::ENTITY_B(),
477                                          aCoinsideLines);
478
479     // Remove auxilary lines
480     if(aCoinsideLines.size() > 2) {
481       std::set<FeaturePtr> aNewLines;
482       for(std::set<FeaturePtr>::iterator anIt = aCoinsideLines.begin(); anIt != aCoinsideLines.end(); ++anIt) {
483         if(!(*anIt)->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value()) {
484           aNewLines.insert(*anIt);
485         }
486       }
487       aCoinsideLines = aNewLines;
488     }
489
490     if(aCoinsideLines.size() != 2) {
491       setError("At selected vertex should be two coincident lines");
492       return;
493     }
494
495     // Store base lines
496     FeaturePtr anOldFeatureA, anOldFeatureB;
497     std::set<FeaturePtr>::iterator aLinesIt = aCoinsideLines.begin();
498     anOldFeatureA = *aLinesIt++;
499     anOldFeatureB = *aLinesIt;
500     aRefListOfBaseLines->append(anOldFeatureA);
501     aRefListOfBaseLines->append(anOldFeatureB);
502
503     // Getting points located at 1/3 of edge length from fillet point
504     std::shared_ptr<GeomAPI_Pnt2d> aPntA, aPntB;
505     getPointOnEdge(anOldFeatureA, aFilletPoint, aPntA);
506     getPointOnEdge(anOldFeatureB, aFilletPoint, aPntB);
507
508     /// Getting distances
509     double aRadius = 1;
510     double aDistanceA = getProjectionDistance(anOldFeatureB, aPntA);
511     double aDistanceB = getProjectionDistance(anOldFeatureA, aPntB);
512     aRadius = aDistanceA < aDistanceB ? aDistanceA / 2.0 : aDistanceB / 2.0;
513
514     std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(data()->attribute(SketchPlugin_Constraint::VALUE()))->setValue(aRadius);
515   }
516 }
517
518 AISObjectPtr SketchPlugin_ConstraintFillet::getAISObject(AISObjectPtr thePrevious)
519 {
520   if (!sketch())
521     return thePrevious;
522
523   AISObjectPtr anAIS = thePrevious;
524   /// TODO: Equal constraint presentation should be put here
525   return anAIS;
526 }
527
528 bool SketchPlugin_ConstraintFillet::isMacro() const
529 {
530   return true;
531 }
532
533
534 // =========   Auxiliary functions   =================
535 void recalculateAttributes(FeaturePtr theNewArc,  const std::string& theNewArcAttribute,
536                            FeaturePtr theFeature, const std::string& theFeatureAttribute)
537 {
538   std::shared_ptr<GeomAPI_Pnt2d> anArcPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
539       theNewArc->attribute(theNewArcAttribute))->pnt();
540   std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
541       theFeature->attribute(theFeatureAttribute))->setValue(anArcPoint->x(), anArcPoint->y());
542 }
543
544 /// \brief Find intersections of lines shifted along normal direction
545 void possibleFilletCenterLineLine(
546     std::shared_ptr<GeomAPI_XY> thePointA, std::shared_ptr<GeomAPI_Dir2d> theDirA,
547     std::shared_ptr<GeomAPI_XY> thePointB, std::shared_ptr<GeomAPI_Dir2d> theDirB,
548     double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
549 {
550   std::shared_ptr<GeomAPI_Dir2d> aDirAT(new GeomAPI_Dir2d(-theDirA->y(), theDirA->x()));
551   std::shared_ptr<GeomAPI_Dir2d> aDirBT(new GeomAPI_Dir2d(-theDirB->y(), theDirB->x()));
552   std::shared_ptr<GeomAPI_XY> aPntA, aPntB;
553   double aDet = theDirA->cross(theDirB);
554   for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
555     aPntA = thePointA->added(aDirAT->xy()->multiplied(aStepA * theRadius));
556     for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
557       aPntB = thePointB->added(aDirBT->xy()->multiplied(aStepB * theRadius));
558       double aVX = aDirAT->xy()->dot(aPntA);
559       double aVY = aDirBT->xy()->dot(aPntB);
560       std::shared_ptr<GeomAPI_XY> aPoint(new GeomAPI_XY(
561           (theDirB->x() * aVX - theDirA->x() * aVY) / aDet,
562           (theDirB->y() * aVX - theDirA->y() * aVY) / aDet));
563       theCenters.push_back(aPoint);
564     }
565   }
566 }
567
568 /// \brief Find intersections of line shifted along normal direction in both sides
569 ///        and a circle with extended radius
570 void possibleFilletCenterLineArc(
571     std::shared_ptr<GeomAPI_XY> theStartLine, std::shared_ptr<GeomAPI_Dir2d> theDirLine,
572     std::shared_ptr<GeomAPI_XY> theCenterArc, double theRadiusArc,
573     double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
574 {
575   std::shared_ptr<GeomAPI_Dir2d> aDirT(new GeomAPI_Dir2d(-theDirLine->y(), theDirLine->x()));
576   std::shared_ptr<GeomAPI_XY> aPnt;
577   double aDirNorm2 = theDirLine->dot(theDirLine);
578   double aRad = 0.0;
579   double aDirX = theDirLine->x();
580   double aDirX2 = theDirLine->x() * theDirLine->x();
581   double aDirY2 = theDirLine->y() * theDirLine->y();
582   double aDirXY = theDirLine->x() * theDirLine->y();
583   for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
584     aPnt = theStartLine->added(aDirT->xy()->multiplied(aStepA * theRadius));
585     double aCoeff = aDirT->xy()->dot(aPnt->decreased(theCenterArc));
586     double aCoeff2 = aCoeff * aCoeff;
587     for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
588       aRad = theRadiusArc + aStepB * theRadius;
589       double aD = aRad * aRad * aDirNorm2 - aCoeff2;
590       if (aD < 0.0)
591         continue;
592       double aDs = sqrt(aD);
593       double x1 = theCenterArc->x() + (aCoeff * aDirT->x() - aDirT->y() * aDs) / aDirNorm2;
594       double x2 = theCenterArc->x() + (aCoeff * aDirT->x() + aDirT->y() * aDs) / aDirNorm2;
595       double y1 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
596           aDirXY * (aPnt->x() - theCenterArc->x()) - theDirLine->y() * aDs) / aDirNorm2;
597       double y2 = (aDirX2 * aPnt->y() + aDirY2 * theCenterArc->y() -
598           aDirXY * (aPnt->x() - theCenterArc->x()) + theDirLine->y() * aDs) / aDirNorm2;
599
600       std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
601       theCenters.push_back(aPoint1);
602       std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
603       theCenters.push_back(aPoint2);
604     }
605   }
606 }
607
608 /// \brief Find intersections of two circles with extended radii
609 void possibleFilletCenterArcArc(
610     std::shared_ptr<GeomAPI_XY> theCenterA, double theRadiusA,
611     std::shared_ptr<GeomAPI_XY> theCenterB, double theRadiusB,
612     double theRadius, std::list< std::shared_ptr<GeomAPI_XY> >& theCenters)
613 {
614   std::shared_ptr<GeomAPI_XY> aCenterDir = theCenterB->decreased(theCenterA);
615   double aCenterDist2 = aCenterDir->dot(aCenterDir);
616   double aCenterDist = sqrt(aCenterDist2);
617
618   double aRadA, aRadB;
619   for (double aStepA = -1.0; aStepA <= 1.0; aStepA += 2.0) {
620     aRadA = theRadiusA + aStepA * theRadius;
621     for (double aStepB = -1.0; aStepB <= 1.0; aStepB += 2.0) {
622       aRadB = theRadiusB + aStepB * theRadius;
623       if (aRadA + aRadB < aCenterDist || fabs(aRadA - aRadB) > aCenterDist)
624         continue; // there is no intersections
625
626       double aMedDist = (aRadA * aRadA - aRadB * aRadB + aCenterDist2) / (2.0 * aCenterDist);
627       double aHeight = sqrt(aRadA * aRadA - aMedDist * aMedDist);
628
629       double x1 = theCenterA->x() + (aMedDist * aCenterDir->x() + aCenterDir->y() * aHeight) / aCenterDist;
630       double y1 = theCenterA->y() + (aMedDist * aCenterDir->y() - aCenterDir->x() * aHeight) / aCenterDist;
631
632       double x2 = theCenterA->x() + (aMedDist * aCenterDir->x() - aCenterDir->y() * aHeight) / aCenterDist;
633       double y2 = theCenterA->y() + (aMedDist * aCenterDir->y() + aCenterDir->x() * aHeight) / aCenterDist;
634
635       std::shared_ptr<GeomAPI_XY> aPoint1(new GeomAPI_XY(x1, y1));
636       theCenters.push_back(aPoint1);
637       std::shared_ptr<GeomAPI_XY> aPoint2(new GeomAPI_XY(x2, y2));
638       theCenters.push_back(aPoint2);
639     }
640   }
641 }
642
643 void calculateFilletCenter(FeaturePtr theFeatureA, FeaturePtr theFeatureB,
644                            double theRadius, bool theNotInversed[2],
645                            std::shared_ptr<GeomAPI_XY>& theCenter,
646                            std::shared_ptr<GeomAPI_XY>& theTangentA,
647                            std::shared_ptr<GeomAPI_XY>& theTangentB)
648 {
649   static const int aNbFeatures = 2;
650   FeaturePtr aFeature[aNbFeatures] = {theFeatureA, theFeatureB};
651   std::shared_ptr<GeomAPI_XY> aStart[aNbFeatures], aEnd[aNbFeatures], aCenter[aNbFeatures];
652   std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, aEndPoint;
653
654   for (int i = 0; i < aNbFeatures; i++) {
655     if (aFeature[i]->getKind() == SketchPlugin_Line::ID()) {
656       aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
657           aFeature[i]->attribute(SketchPlugin_Line::START_ID()));
658       aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
659           aFeature[i]->attribute(SketchPlugin_Line::END_ID()));
660     } else if (aFeature[i]->getKind() == SketchPlugin_Arc::ID()) {
661       aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
662           aFeature[i]->attribute(SketchPlugin_Arc::START_ID()));
663       aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
664           aFeature[i]->attribute(SketchPlugin_Arc::END_ID()));
665       aCenter[i] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
666           aFeature[i]->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt()->xy();
667     } else
668       return;
669     aStart[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
670         new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()) :
671         new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()));
672     aEnd[i] = std::shared_ptr<GeomAPI_XY>(theNotInversed[i] ?
673         new GeomAPI_XY(aEndPoint->x(), aEndPoint->y()) :
674         new GeomAPI_XY(aStartPoint->x(), aStartPoint->y()));
675   }
676
677   if (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
678       theFeatureB->getKind() == SketchPlugin_Line::ID()) {
679     std::shared_ptr<GeomAPI_Dir2d> aDir[2];
680     std::shared_ptr<GeomAPI_Dir2d> aDirT[2];
681     for (int i = 0; i < aNbFeatures; i++) {
682       aDir[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(aEnd[i]->decreased(aStart[i])));
683       aDirT[i] = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d(-aDir[i]->y(), aDir[i]->x()));
684     }
685
686     // get and filter possible centers
687     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
688     possibleFilletCenterLineLine(aStart[0], aDir[0], aStart[1], aDir[1], theRadius, aSuspectCenters);
689     double aDot = 0.0;
690     std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
691     for (; anIt != aSuspectCenters.end(); anIt++) {
692       aDot = aDirT[0]->xy()->dot(aStart[0]->decreased(*anIt));
693       theTangentA = (*anIt)->added(aDirT[0]->xy()->multiplied(aDot));
694       if (theTangentA->decreased(aStart[0])->dot(aDir[0]->xy()) < 0.0)
695         continue; // incorrect position
696       aDot = aDirT[1]->xy()->dot(aStart[1]->decreased(*anIt));
697       theTangentB = (*anIt)->added(aDirT[1]->xy()->multiplied(aDot));
698       if (theTangentB->decreased(aStart[1])->dot(aDir[1]->xy()) < 0.0)
699         continue; // incorrect position
700       // the center is found, stop searching
701       theCenter = *anIt;
702       return;
703     }
704   } else if ((theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
705       theFeatureB->getKind() == SketchPlugin_Line::ID()) || 
706       (theFeatureA->getKind() == SketchPlugin_Line::ID() &&
707       theFeatureB->getKind() == SketchPlugin_Arc::ID())) {
708     int aLineInd = theFeatureA->getKind() == SketchPlugin_Line::ID() ? 0 : 1;
709     double anArcRadius = aStart[1-aLineInd]->distance(aCenter[1-aLineInd]);
710     std::shared_ptr<GeomAPI_Dir2d> aDirLine = std::shared_ptr<GeomAPI_Dir2d>(
711         new GeomAPI_Dir2d(aEnd[aLineInd]->decreased(aStart[aLineInd])));
712     std::shared_ptr<GeomAPI_Dir2d> aDirT = std::shared_ptr<GeomAPI_Dir2d>(
713         new GeomAPI_Dir2d(-aDirLine->y(), aDirLine->x()));
714
715     std::shared_ptr<GeomAPI_Dir2d> aStartArcDir = std::shared_ptr<GeomAPI_Dir2d>(
716         new GeomAPI_Dir2d(aStart[1-aLineInd]->decreased(aCenter[1-aLineInd])));
717     std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
718         new GeomAPI_Dir2d(aEnd[1-aLineInd]->decreased(aCenter[1-aLineInd])));
719     double anArcAngle = aEndArcDir->angle(aStartArcDir);
720
721     // get possible centers and filter them
722     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
723     possibleFilletCenterLineArc(aStart[aLineInd], aDirLine, aCenter[1-aLineInd], anArcRadius, theRadius, aSuspectCenters);
724     double aDot = 0.0;
725     // the line is forward into the arc
726     double innerArc = aCenter[1-aLineInd]->decreased(aStart[aLineInd])->dot(aDirLine->xy());
727     std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
728     // The possible centers are ranged by their positions.
729     // If the point is not satisfy one of criteria, the weight is decreased with penalty.
730     int aBestWeight = 0;
731     std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
732     for (; anIt != aSuspectCenters.end(); anIt++) {
733       int aWeight = 2;
734       aDot = aDirT->xy()->dot(aStart[aLineInd]->decreased(*anIt));
735       aLineTgPoint = (*anIt)->added(aDirT->xy()->multiplied(aDot));
736       // Check the point is placed on the correct arc (penalty if false)
737       if (aCenter[1-aLineInd]->distance(*anIt) * innerArc > anArcRadius * innerArc)
738         aWeight -= 1;
739       std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
740           new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1-aLineInd])));
741       double aCurAngle = aCurDir->angle(aStartArcDir);
742       if (anArcAngle < 0.0) aCurAngle *= -1.0;
743       if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle))
744         continue;
745       if (aWeight > aBestWeight)
746         aBestWeight = aWeight;
747       else if (aWeight < aBestWeight ||
748                aStart[aLineInd]->distance(*anIt) >
749                aStart[aLineInd]->distance(theCenter)) // <-- take closer point
750         continue;
751       // the center is found, stop searching
752       theCenter = *anIt;
753       anArcTgPoint = aCenter[1-aLineInd]->added(aCurDir->xy()->multiplied(anArcRadius));
754       if (theFeatureA->getKind() == SketchPlugin_Line::ID()) {
755         theTangentA = aLineTgPoint;
756         theTangentB = anArcTgPoint;
757       } else {
758         theTangentA = anArcTgPoint;
759         theTangentB = aLineTgPoint;
760       }
761       //return;
762     }
763   } else if (theFeatureA->getKind() == SketchPlugin_Arc::ID() &&
764       theFeatureB->getKind() == SketchPlugin_Arc::ID()) {
765     double anArcRadius[aNbFeatures];
766     double anArcAngle[aNbFeatures];
767     std::shared_ptr<GeomAPI_Dir2d> aStartArcDir[aNbFeatures];
768     for (int i = 0; i < aNbFeatures; i++) {
769       anArcRadius[i] = aStart[i]->distance(aCenter[i]);
770       aStartArcDir[i] = std::shared_ptr<GeomAPI_Dir2d>(
771           new GeomAPI_Dir2d(aStart[i]->decreased(aCenter[i])));
772       std::shared_ptr<GeomAPI_Dir2d> aEndArcDir = std::shared_ptr<GeomAPI_Dir2d>(
773           new GeomAPI_Dir2d(aEnd[i]->decreased(aCenter[i])));
774       anArcAngle[i] = aEndArcDir->angle(aStartArcDir[i]);
775     }
776
777     // get and filter possible centers
778     std::list< std::shared_ptr<GeomAPI_XY> > aSuspectCenters;
779     possibleFilletCenterArcArc(aCenter[0], anArcRadius[0], aCenter[1], anArcRadius[1], theRadius, aSuspectCenters);
780     double aDot = 0.0;
781     std::shared_ptr<GeomAPI_XY> aLineTgPoint, anArcTgPoint;
782     std::list< std::shared_ptr<GeomAPI_XY> >::iterator anIt = aSuspectCenters.begin();
783     for (; anIt != aSuspectCenters.end(); anIt++) {
784       std::shared_ptr<GeomAPI_Dir2d> aCurDir = std::shared_ptr<GeomAPI_Dir2d>(
785           new GeomAPI_Dir2d((*anIt)->decreased(aCenter[0])));
786       double aCurAngle = aCurDir->angle(aStartArcDir[0]);
787       if (anArcAngle[0] < 0.0) aCurAngle *= -1.0;
788       if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[0]))
789         continue; // incorrect position
790       theTangentA = aCenter[0]->added(aCurDir->xy()->multiplied(anArcRadius[0]));
791
792       aCurDir = std::shared_ptr<GeomAPI_Dir2d>(new GeomAPI_Dir2d((*anIt)->decreased(aCenter[1])));
793       aCurAngle = aCurDir->angle(aStartArcDir[1]);
794       if (anArcAngle[1] < 0.0) aCurAngle *= -1.0;
795       if (aCurAngle < 0.0 || aCurAngle > fabs(anArcAngle[1]))
796         continue; // incorrect position
797       theTangentB = aCenter[1]->added(aCurDir->xy()->multiplied(anArcRadius[1]));
798
799       // the center is found, stop searching
800       theCenter = *anIt;
801       return;
802     }
803   }
804 }
805
806 void getPointOnEdge(const FeaturePtr theFeature,
807                     const std::shared_ptr<GeomAPI_Pnt2d> theFilletPoint,
808                     std::shared_ptr<GeomAPI_Pnt2d>& thePoint) {
809   if(theFeature->getKind() == SketchPlugin_Line::ID()) {
810     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
811       theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
812     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
813       theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
814     if(aPntStart->distance(theFilletPoint) > 1.e-7) {
815       aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
816         theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
817       aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
818         theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
819     }
820     thePoint.reset( new GeomAPI_Pnt2d(aPntStart->xy()->added( aPntEnd->xy()->decreased( aPntStart->xy() )->multiplied(1.0 / 3.0) ) ) );
821   } else {
822     std::shared_ptr<GeomAPI_Pnt2d> aPntTemp;
823     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
824       theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
825     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
826       theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
827     if(theFeature->attribute(SketchPlugin_Arc::INVERSED_ID())) {
828       aPntTemp = aPntStart;
829       aPntStart = aPntEnd;
830       aPntEnd = aPntTemp;
831     }
832     std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
833       theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
834     std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
835     double aStartParameter(0), anEndParameter(0);
836     aCirc->parameter(aPntStart, paramTolerance, aStartParameter);
837     aCirc->parameter(aPntEnd, paramTolerance, anEndParameter);
838     if(aPntStart->distance(theFilletPoint) > tolerance) {
839       double aTmpParameter = aStartParameter;
840       aStartParameter = anEndParameter;
841       anEndParameter = aTmpParameter;
842     }
843     double aPntParameter = aStartParameter + (anEndParameter - aStartParameter) / 3.0;
844     aCirc->D0(aPntParameter, thePoint);
845   }
846 }
847
848 double getProjectionDistance(const FeaturePtr theFeature,
849                              const std::shared_ptr<GeomAPI_Pnt2d> thePoint)
850 {
851   std::shared_ptr<GeomAPI_Pnt2d> aProjectPnt;
852   if(theFeature->getKind() == SketchPlugin_Line::ID()) {
853     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
854       theFeature->attribute(SketchPlugin_Line::START_ID()))->pnt();
855     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
856       theFeature->attribute(SketchPlugin_Line::END_ID()))->pnt();
857     std::shared_ptr<GeomAPI_Lin2d> aLin(new GeomAPI_Lin2d(aPntStart, aPntEnd));
858     aProjectPnt = aLin->project(thePoint);
859   } else {
860     std::shared_ptr<GeomAPI_Pnt2d> aPntStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
861       theFeature->attribute(SketchPlugin_Arc::START_ID()))->pnt();
862     std::shared_ptr<GeomAPI_Pnt2d> aPntEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
863       theFeature->attribute(SketchPlugin_Arc::END_ID()))->pnt();
864     std::shared_ptr<GeomAPI_Pnt2d> aCenterPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
865       theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
866     std::shared_ptr<GeomAPI_Circ2d> aCirc(new GeomAPI_Circ2d(aCenterPnt, aPntStart));
867     aProjectPnt = aCirc->project(thePoint);
868   }
869   if(aProjectPnt.get()) {
870     return aProjectPnt->distance(thePoint);
871   }
872   return -1;
873 }