Salome HOME
4ac4418410890de78bb75ddba68f0a2d32d3f17a
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_ConstraintSplit.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:    SketchPlugin_ConstraintSplit.cpp
4 // Created: 25 Aug 2016
5 // Author:  Natalia ERMOLAEVA
6
7 #include "SketchPlugin_ConstraintSplit.h"
8
9 #include <GeomAPI_Dir2d.h>
10 #include <GeomAPI_Pnt2d.h>
11 #include <GeomAPI_XY.h>
12 #include <GeomDataAPI_Point2D.h>
13 #include <GeomAlgoAPI_ShapeTools.h>
14
15 #include <ModelAPI_AttributeReference.h>
16 #include <ModelAPI_AttributeString.h>
17 #include <ModelAPI_AttributeRefAttr.h>
18 #include <ModelAPI_Tools.h>
19
20 #include <ModelAPI_Validator.h>
21 #include <ModelAPI_Session.h>
22 #include <ModelAPI_AttributeDouble.h>
23
24 #include <SketchPlugin_Line.h>
25 #include <SketchPlugin_Arc.h>
26 #include <SketchPlugin_Circle.h>
27 #include <SketchPlugin_ConstraintCoincidence.h>
28 #include <SketchPlugin_ConstraintEqual.h>
29 #include <SketchPlugin_ConstraintParallel.h>
30 #include <SketchPlugin_ConstraintTangent.h>
31 #include <SketchPlugin_ConstraintLength.h>
32 #include <SketchPlugin_ConstraintMirror.h>
33 #include <SketchPlugin_MultiRotation.h>
34 #include <SketchPlugin_MultiTranslation.h>
35
36 #include <ModelAPI_Events.h>
37 #include <SketchPlugin_Line.h>
38 #include <SketchPlugin_Arc.h>
39 #include <SketchPlugin_Circle.h>
40
41 #include <ModelGeomAlgo_Point2D.h>
42 #include <Events_Loop.h>
43
44 #include <cmath>
45
46 //#define DEBUG_SPLIT
47 #ifdef DEBUG_SPLIT
48 #include <iostream>
49 #endif
50
51 static const double PI = 3.141592653589793238463;
52
53 SketchPlugin_ConstraintSplit::SketchPlugin_ConstraintSplit()
54 {
55 }
56
57 void SketchPlugin_ConstraintSplit::initAttributes()
58 {
59   data()->addAttribute(SketchPlugin_Constraint::VALUE(), ModelAPI_AttributeReference::typeId());
60   data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefAttr::typeId());
61   data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefAttr::typeId());
62 }
63
64 void SketchPlugin_ConstraintSplit::execute()
65 {
66   std::shared_ptr<ModelAPI_Data> aData = data();
67
68   // Check the base objects are initialized.
69   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
70                                             aData->attribute(SketchPlugin_Constraint::VALUE()));
71   if(!aBaseObjectAttr->isInitialized()) {
72     setError("Error: Base object is not initialized.");
73     return;
74   }
75   AttributePoint2DPtr aFirstPointAttrOfSplit = 
76     getPointOfRefAttr(aData->attribute(SketchPlugin_Constraint::ENTITY_A()));
77   AttributePoint2DPtr aSecondPointAttrOfSplit = 
78     getPointOfRefAttr(aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
79   if (!aFirstPointAttrOfSplit.get() || !aFirstPointAttrOfSplit->isInitialized() ||
80       !aSecondPointAttrOfSplit.get() || !aSecondPointAttrOfSplit->isInitialized()) {
81     setError("Error: Sub-shape is not initialized.");
82     return;
83   }
84
85   // Wait all constraints being created, then send update events
86   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
87   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
88   if (isUpdateFlushed)
89     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
90
91
92   // Find feature constraints
93   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
94   ResultPtr aBaseFeatureResult = getFeatureResult(aBaseFeature);
95   std::set<FeaturePtr> aFeaturesToDelete, aFeaturesToUpdate;
96   std::map<FeaturePtr, IdToPointPair> aTangentFeatures;
97   std::map<FeaturePtr, IdToPointPair> aCoincidenceToFeature;
98   getConstraints(aFeaturesToDelete, aFeaturesToUpdate, aTangentFeatures, aCoincidenceToFeature);
99
100   std::map<AttributePtr, std::list<AttributePtr> > aBaseRefAttributes;
101   std::list<AttributePtr> aRefsToFeature;
102   getRefAttributes(aBaseFeature, aBaseRefAttributes, aRefsToFeature);
103
104   std::map<AttributePtr, AttributePtr> aBasePointModifiedAttributes;
105
106 #ifdef DEBUG_SPLIT
107   std::cout << std::endl;
108   std::cout << "SketchPlugin_ConstraintSplit::execute()" << std::endl;
109   std::cout << std::endl;
110
111   SketchPlugin_Sketch* aSketch = sketch();
112   std::cout << "SKETCH FEATURES (before split) [" << aSketch->numberOfSubs() << "]:" << std::endl;
113   for (int i = 0, aNbSubs = aSketch->numberOfSubs(); i < aNbSubs; i++) {
114     std::cout << getFeatureInfo(aSketch->subFeature(i), false) << std::endl;
115   }
116
117   std::cout << std::endl;
118   std::cout << "---- IN PARAMETERS ----" << std::endl;
119   std::cout << "Base feature:" << getFeatureInfo(aBaseFeature) << std::endl;
120   std::cout << std::endl;
121
122   if (!aCoincidenceToFeature.empty()) {
123     std::cout << "Coincidences to base feature[" << 
124       aCoincidenceToFeature.size() << "]: " << std::endl;
125     std::map<FeaturePtr, IdToPointPair>::const_iterator anIt = aCoincidenceToFeature.begin(),
126                                                         aLast = aCoincidenceToFeature.end();
127     for (int i = 1; anIt != aLast; anIt++, i++) {
128       FeaturePtr aFeature = (*anIt).first;
129       std::string anAttributeId = (*anIt).second.first;
130       std::shared_ptr<GeomDataAPI_Point2D> aPointAttr = (*anIt).second.second;
131
132       std::cout << i << "-" << getFeatureInfo(aFeature) << std::endl;
133       std::cout <<     " -Attribute to correct:" << anAttributeId << std::endl;
134       std::cout <<     " -Point attribute:" << 
135         ModelGeomAlgo_Point2D::getPointAttributeInfo(aPointAttr) << std::endl;
136     }
137   }
138
139   if (!aTangentFeatures.empty()) {
140     std::cout << std::endl;
141     std::cout << "Tangencies to base feature[" << aTangentFeatures.size() << "]: " << std::endl;
142     std::map<FeaturePtr, IdToPointPair>::const_iterator anIt = aTangentFeatures.begin(),
143                                                         aLast = aTangentFeatures.end();
144     for (int i = 1; anIt != aLast; anIt++, i++) {
145       FeaturePtr aFeature = (*anIt).first;
146       std::string anAttributeId = (*anIt).second.first;
147       std::shared_ptr<GeomDataAPI_Point2D> aPointAttr = (*anIt).second.second;
148
149       std::cout << i << "-" << getFeatureInfo(aFeature) << std::endl;
150       std::cout <<     " -Attribute to correct:" << anAttributeId << std::endl;
151       std::cout <<     " -Point attribute:" << 
152         ModelGeomAlgo_Point2D::getPointAttributeInfo(aPointAttr) << std::endl;
153     }
154   }
155
156   std::map<AttributePtr, std::list<AttributePtr> >::const_iterator 
157     aRefIt = aBaseRefAttributes.begin(), aRefLast = aBaseRefAttributes.end();
158   std::cout << std::endl << "References to attributes of base feature [" << 
159     aBaseRefAttributes.size() << "]" << std::endl;
160   for (; aRefIt != aRefLast; aRefIt++) {
161     AttributePtr aBaseAttr = aRefIt->first;
162     std::list<AttributePtr> aRefAttributes = aRefIt->second;
163     std::string aRefsInfo;
164     std::list<AttributePtr>::const_iterator aRefAttrIt = aRefAttributes.begin(),
165                                             aRefAttrLast = aRefAttributes.end();
166     for (; aRefAttrIt != aRefAttrLast; aRefAttrIt++) {
167       if (!aRefsInfo.empty())
168         aRefsInfo.append(",");
169       AttributePtr aRAttr = *aRefAttrIt;
170       aRefsInfo.append(aRAttr->id());
171       FeaturePtr aRFeature = ModelAPI_Feature::feature(aRAttr->owner());
172       aRefsInfo.append("(" + aRFeature->name() + ") ");
173     }
174     std::shared_ptr<GeomDataAPI_Point2D> aPointAttr = 
175       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aBaseAttr);
176     std::cout << aPointAttr->id().c_str() << 
177       ": " << "[" << aRefAttributes.size() << "] " << aRefsInfo << std::endl;
178   }
179   std::cout << std::endl;
180   std::cout << std::endl << "References to base feature [" << 
181     aRefsToFeature.size() << "]" << std::endl;
182   std::list<AttributePtr>::const_iterator aRefAttrIt = aRefsToFeature.begin(),
183                                           aRefAttrLast = aRefsToFeature.end();
184   std::string aRefsInfo;
185   for (; aRefAttrIt != aRefAttrLast; aRefAttrIt++) {
186     if (!aRefsInfo.empty())
187       aRefsInfo.append(",");
188     AttributePtr aRAttr = *aRefAttrIt;
189     aRefsInfo.append(aRAttr->id());
190     FeaturePtr aRFeature = ModelAPI_Feature::feature(aRAttr->owner());
191     aRefsInfo.append("(" + aRFeature->name() + ") ");
192   }
193   std::cout << "[" << aRefsToFeature.size() << "] " << aRefsInfo << std::endl;
194
195
196   std::cout << std::endl;
197   std::cout << "---- SPLIT ----" << std::endl;
198   std::cout << std::endl;
199 #endif
200
201   std::string aFeatureKind = aBaseFeature->getKind();
202   FeaturePtr aSplitFeature, anAfterFeature;
203   std::set<AttributePoint2DPtr> aFurtherCoincidences;
204   std::set<FeaturePtr> aCreatedFeatures;
205   std::set<std::pair<AttributePtr, AttributePtr>> aModifiedAttributes;
206   if (aFeatureKind == SketchPlugin_Line::ID())
207     splitLine(aSplitFeature, aBaseFeature, anAfterFeature, aFurtherCoincidences, aCreatedFeatures,
208               aModifiedAttributes);
209   else if (aFeatureKind == SketchPlugin_Arc::ID())
210     splitArc(aSplitFeature, aBaseFeature, anAfterFeature, aFurtherCoincidences, aCreatedFeatures,
211              aModifiedAttributes);
212   if (aFeatureKind == SketchPlugin_Circle::ID()) {
213     FeaturePtr aCircleFeature = aBaseFeature;
214     splitCircle(aSplitFeature, aBaseFeature, anAfterFeature, aFurtherCoincidences, 
215       aCreatedFeatures, aModifiedAttributes);
216
217     updateRefFeatureConstraints(getFeatureResult(aBaseFeature), aRefsToFeature);
218
219     aFeaturesToDelete.insert(aCircleFeature);
220     // as circle is removed, temporary fill this attribute*/
221     aBaseObjectAttr->setObject(ResultPtr());
222   }
223
224 #ifdef DEBUG_SPLIT
225   std::cout << "---- OUT PARAMETERS ----" << std::endl;
226   std::cout << "Base modified feature:" << getFeatureInfo(aBaseFeature) << std::endl;
227   std::cout << "Split feature:" << getFeatureInfo(aSplitFeature) << std::endl;
228   std::cout << "After feature:" << getFeatureInfo(anAfterFeature) << std::endl;
229   std::cout << std::endl;
230
231   std::cout << "Created features by split:[" << aCreatedFeatures.size() << "]" << std::endl;
232   std::set<FeaturePtr>::const_iterator aFIt = aCreatedFeatures.begin(),
233                                        aFLast = aCreatedFeatures.end();
234   for (; aFIt != aFLast; aFIt++) {
235     std::cout << getFeatureInfo(*aFIt) << std::endl;
236   }
237   std::cout << std::endl;
238
239   std::cout << "Attributes for further Coincidences:" << std::endl;
240   std::set<AttributePoint2DPtr>::const_iterator anIt = aFurtherCoincidences.begin(),
241                                                 aLast = aFurtherCoincidences.end();
242   for (; anIt != aLast; anIt++) {
243     AttributePtr anAttribute = *anIt;
244     FeaturePtr aFeature = ModelAPI_Feature::feature(anAttribute->owner());
245     std::cout << ModelGeomAlgo_Point2D::getPointAttributeInfo(anAttribute)
246               << " [" << getFeatureInfo(aFeature, false) << "]" << std::endl;
247   }
248
249   std::cout << "Modifed attributes (constraints to attributes are moved here):" << std::endl;
250   std::set<std::pair<AttributePtr, AttributePtr> >::const_iterator 
251     aPIt = aModifiedAttributes.begin(), aPLast = aModifiedAttributes.end();
252   std::string aResInfo;
253   for (; aPIt != aPLast; aPIt++) {
254     if (!aResInfo.empty())
255       aResInfo += "\n";
256
257     std::pair<AttributePtr, AttributePtr> aPair = *aPIt;
258
259     AttributePtr anAttr = aPair.first;
260     aResInfo.append(anAttr->id());
261     FeaturePtr aFeature = ModelAPI_Feature::feature(anAttr->owner());
262     aResInfo.append("(" + aFeature->name() + ") ");
263
264     aResInfo.append("  - is modified to -  ");
265
266     anAttr = aPair.second;
267     aResInfo.append(anAttr->id());
268     aFeature = ModelAPI_Feature::feature(anAttr->owner());
269     aResInfo.append("(" + aFeature->name() + ") ");
270   }
271   std::cout << aResInfo << std::endl;
272 #endif
273
274   std::set<ResultPtr> aFeatureResults;
275   aFeatureResults.insert(getFeatureResult(aBaseFeature));
276   if (anAfterFeature.get() && anAfterFeature != aBaseFeature)
277     aFeatureResults.insert(getFeatureResult(anAfterFeature));
278
279   // coincidence to feature
280   updateCoincidenceConstraintsToFeature(aCoincidenceToFeature, aFurtherCoincidences,
281                                         aFeatureResults);
282   // tangency
283   updateTangentConstraintsToFeature(aTangentFeatures, aFurtherCoincidences);
284
285   updateRefAttConstraints(aBaseRefAttributes, aModifiedAttributes);
286
287   // delete constraints
288 #ifdef DEBUG_SPLIT
289   std::cout << "remove features and references:" << std::endl;
290   std::set<FeaturePtr>::const_iterator aDIt = aFeaturesToDelete.begin(),
291                                        aDLast = aFeaturesToDelete.end();
292   for (; aDIt != aDLast; aDIt++) {
293     std::cout << getFeatureInfo(*aDIt, false) << std::endl;
294     std::cout << std::endl;
295   }
296 #endif
297   ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToDelete);
298
299 #ifdef DEBUG_SPLIT
300   std::cout << "update features after split:" << std::endl;
301   std::set<FeaturePtr>::const_iterator anUIt = aFeaturesToUpdate.begin(),
302                                        anULast = aFeaturesToUpdate.end();
303   for (; anUIt != anULast; anUIt++) {
304     std::cout << getFeatureInfo(*anUIt, false) << std::endl;
305     std::cout << std::endl;
306   }
307 #endif
308   updateFeaturesAfterSplit(aFeaturesToUpdate);
309
310   // Send events to update the sub-features by the solver.
311   if(isUpdateFlushed) {
312     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
313   }
314
315 #ifdef DEBUG_SPLIT
316   std::cout << "SKETCH FEATURES (after split) [" << aSketch->numberOfSubs() << "]:" << std::endl;
317   for (int i = 0, aNbSubs = aSketch->numberOfSubs(); i < aNbSubs; i++) {
318     std::cout << getFeatureInfo(aSketch->subFeature(i), false) << std::endl;
319   }
320 #endif
321 }
322
323 bool SketchPlugin_ConstraintSplit::isMacro() const
324 {
325   return true;
326 }
327
328 AISObjectPtr SketchPlugin_ConstraintSplit::getAISObject(AISObjectPtr thePrevious)
329 {
330   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
331                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
332   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
333
334   AttributePoint2DPtr aFirstPointAttrOfSplit = getPointOfRefAttr(
335                                         data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
336   AttributePoint2DPtr aSecondPointAttrOfSplit = getPointOfRefAttr(
337                                         data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
338
339   if (aBaseObjectAttr->isInitialized() && aBaseFeature.get() &&
340       aFirstPointAttrOfSplit->isInitialized() &&
341       aSecondPointAttrOfSplit->isInitialized()) {
342
343     ResultPtr aResult = getFeatureResult(aBaseFeature);
344     GeomShapePtr aBaseShape = aResult->shape();
345     std::list<std::shared_ptr<GeomAPI_Pnt> > aPoints;
346
347     std::shared_ptr<GeomAPI_Pnt2d> aStartPnt2d = aFirstPointAttrOfSplit->pnt();
348     std::shared_ptr<GeomAPI_Pnt> aStartPoint = sketch()->to3D(aStartPnt2d->x(), aStartPnt2d->y());
349     aPoints.push_back(aStartPoint);
350
351     std::shared_ptr<GeomAPI_Pnt2d> aSecondPnt2d = aSecondPointAttrOfSplit->pnt();
352     std::shared_ptr<GeomAPI_Pnt> aSecondPoint = 
353       sketch()->to3D(aSecondPnt2d->x(), aSecondPnt2d->y());
354     aPoints.push_back(aSecondPoint);
355
356     std::set<std::shared_ptr<GeomAPI_Shape> > aSplitShapes;
357
358     GeomAlgoAPI_ShapeTools::splitShape(aBaseShape, aPoints, aSplitShapes);
359     std::shared_ptr<GeomAPI_Shape> aShape = 
360       GeomAlgoAPI_ShapeTools::findShape(aPoints, aSplitShapes);
361
362     AISObjectPtr anAIS = thePrevious;
363     if (aShape) {
364       if (!anAIS)
365         anAIS = AISObjectPtr(new GeomAPI_AISObject);
366       anAIS->createShape(aShape);
367       anAIS->setWidth(5);
368       std::vector<int> aColor;
369       aColor = Config_PropManager::color("Visualization", "sketch_entity_color",
370                                           SKETCH_ENTITY_COLOR);
371      anAIS->setColor(aColor[0], aColor[1], aColor[2]);
372     }
373     return anAIS;
374   }
375   return AISObjectPtr();
376 }
377
378 std::shared_ptr<GeomDataAPI_Point2D> SketchPlugin_ConstraintSplit::getPointOfRefAttr(
379                                                       const AttributePtr& theAttribute)
380 {
381   AttributePoint2DPtr aPointAttribute;
382
383   if (theAttribute->attributeType() == ModelAPI_AttributeRefAttr::typeId()) {
384     AttributeRefAttrPtr aRefAttr = 
385       std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(theAttribute);
386     if (aRefAttr.get() && aRefAttr->isInitialized()) {
387       AttributePtr anAttribute = aRefAttr->attr();
388       if (anAttribute.get() && anAttribute->attributeType() == GeomDataAPI_Point2D::typeId())
389         aPointAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttribute);
390     }
391   }
392   return aPointAttribute;
393 }
394
395 void SketchPlugin_ConstraintSplit::getFeaturePoints(AttributePoint2DPtr& theStartPointAttr,
396                                                     AttributePoint2DPtr& theEndPointAttr)
397 {
398   AttributePoint2DPtr aPointAttribute;
399
400   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
401                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
402   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
403
404   std::string aFeatureKind = aBaseFeature->getKind();
405   std::string aStartAttributeName, anEndAttributeName;
406   if (aFeatureKind == SketchPlugin_Line::ID()) {
407     aStartAttributeName = SketchPlugin_Line::START_ID();
408     anEndAttributeName = SketchPlugin_Line::END_ID();
409   }
410   else if (aFeatureKind == SketchPlugin_Arc::ID()) {
411     aStartAttributeName = SketchPlugin_Arc::START_ID();
412     anEndAttributeName = SketchPlugin_Arc::END_ID();
413   }
414   if (!aStartAttributeName.empty() && !anEndAttributeName.empty()) {
415     theStartPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
416                                          aBaseFeature->attribute(aStartAttributeName));
417     theEndPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
418                                          aBaseFeature->attribute(anEndAttributeName));
419   }
420 }
421
422 void SketchPlugin_ConstraintSplit::getConstraints(std::set<FeaturePtr>& theFeaturesToDelete,
423                                       std::set<FeaturePtr>& theFeaturesToUpdate,
424                                       std::map<FeaturePtr, IdToPointPair>& theTangentFeatures,
425                                       std::map<FeaturePtr, IdToPointPair>& theCoincidenceToFeature)
426 {
427   std::shared_ptr<ModelAPI_Data> aData = data();
428
429   // Check the base objects are initialized.
430   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
431                                             aData->attribute(SketchPlugin_Constraint::VALUE()));
432   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
433   ResultPtr aBaseFeatureResult = getFeatureResult(aBaseFeature);
434
435   std::set<AttributePtr> aRefsList = aBaseFeatureResult->data()->refsToMe();
436   std::set<AttributePtr> aFRefsList = aBaseFeature->data()->refsToMe();
437   aRefsList.insert(aFRefsList.begin(), aFRefsList.end());
438
439   std::set<AttributePtr>::const_iterator aIt;
440   for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
441     std::shared_ptr<ModelAPI_Attribute> aAttr = (*aIt);
442     FeaturePtr aRefFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aAttr->owner());
443     std::string aRefFeatureKind = aRefFeature->getKind();
444     if (aRefFeatureKind == SketchPlugin_ConstraintMirror::ID() ||
445         aRefFeatureKind == SketchPlugin_MultiRotation::ID() ||
446         aRefFeatureKind == SketchPlugin_MultiTranslation::ID())
447       theFeaturesToDelete.insert(aRefFeature);
448     else if (aRefFeatureKind == SketchPlugin_ConstraintLength::ID())
449       theFeaturesToUpdate.insert(aRefFeature);
450     else if (aRefFeatureKind == SketchPlugin_ConstraintTangent::ID()) {
451       if (aBaseFeature->getKind() == SketchPlugin_Circle::ID()) /// TEMPORARY limitaion
452         /// until tangency between arc and line is implemented
453         theFeaturesToDelete.insert(aRefFeature); 
454       else {
455         std::string anAttributeToBeModified;
456         AttributePoint2DPtr aTangentPoint;
457         ObjectPtr aResult1 = aRefFeature->refattr(SketchPlugin_Constraint::ENTITY_A())->object();
458         ObjectPtr aResult2 = aRefFeature->refattr(SketchPlugin_Constraint::ENTITY_B())->object();
459         if (aResult1.get() && aResult2.get()) {
460           FeaturePtr aCoincidenceFeature = 
461             SketchPlugin_ConstraintCoincidence::findCoincidenceFeature
462                                                        (ModelAPI_Feature::feature(aResult1),
463                                                         ModelAPI_Feature::feature(aResult2));
464           // get the point not lying on the splitting feature
465           for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
466             AttributeRefAttrPtr aRefAttr = aCoincidenceFeature->refattr(ATTRIBUTE(i));
467             if (!aRefAttr || aRefAttr->isObject())
468               continue;
469             AttributePoint2DPtr aPoint =
470                 std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
471             if (!aPoint)
472               continue;
473             if (aPoint->owner() != aBaseFeature) {
474               aTangentPoint = aPoint;
475               break;
476             }
477           }
478         }
479         if (aTangentPoint.get()) {
480           FeaturePtr aFeature1 = ModelAPI_Feature::feature(aResult1);
481           std::string anAttributeToBeModified = aFeature1 == aBaseFeature
482                        ? SketchPlugin_Constraint::ENTITY_A() : SketchPlugin_Constraint::ENTITY_B();
483           theTangentFeatures[aRefFeature] = std::make_pair(anAttributeToBeModified, aTangentPoint);
484         }
485         else /// there is not coincident point between tangent constraint
486           theFeaturesToDelete.insert(aRefFeature); 
487       }
488     }
489     else if (aRefFeatureKind == SketchPlugin_ConstraintCoincidence::ID()) {
490       std::string anAttributeToBeModified;
491       AttributePoint2DPtr aCoincidentPoint;
492       AttributeRefAttrPtr anAttrA = aRefFeature->refattr(SketchPlugin_Constraint::ENTITY_A());
493       AttributeRefAttrPtr anAttrB = aRefFeature->refattr(SketchPlugin_Constraint::ENTITY_B());
494       bool isToFeature = false;
495       if (anAttrA->isObject() || anAttrB->isObject()) { /// coincidence to base feature
496         FeaturePtr aFeature = anAttrA->isObject() ? ModelAPI_Feature::feature(anAttrA->object())
497                                                   : FeaturePtr();
498         isToFeature = aFeature.get() && aFeature == aBaseFeature;
499         anAttributeToBeModified = anAttrA->id();
500         if (!isToFeature) {
501           aFeature = anAttrB->isObject() ? ModelAPI_Feature::feature(anAttrB->object())
502                                          : FeaturePtr();
503           isToFeature = aFeature.get() && aFeature == aBaseFeature;
504           anAttributeToBeModified = anAttrB->id();
505         }
506         if (isToFeature)
507           aCoincidentPoint = SketchPlugin_ConstraintCoincidence::getPoint(aRefFeature);
508       }
509       if (!isToFeature) { /// coincidence to point on base feature
510         AttributePtr anAttribute;
511
512         if (!anAttrA->isObject()) {
513           AttributePtr aCurAttribute = anAttrA->attr();
514           if (aCurAttribute.get()) {
515             FeaturePtr aCurFeature = ModelAPI_Feature::feature(aCurAttribute->owner());
516             if (aCurFeature.get() && aCurFeature == aBaseFeature) {
517               anAttribute = anAttrB->attr();
518               anAttributeToBeModified = anAttrA->id();
519             }
520           }
521         }
522         if (!anAttribute.get() && !anAttrB->isObject()) {
523           AttributePtr aCurAttribute = anAttrB->attr();
524           if (aCurAttribute.get()) {
525             FeaturePtr aCurFeature = ModelAPI_Feature::feature(aCurAttribute->owner());
526             if (aCurFeature.get() && aCurFeature == aBaseFeature) {
527               anAttribute = anAttrA->attr();
528               anAttributeToBeModified = anAttrB->id();
529             }
530           }
531         }
532         if (anAttribute.get())
533           aCoincidentPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(anAttribute);
534       }
535       if (aCoincidentPoint.get() && isToFeature)
536         theCoincidenceToFeature[aRefFeature] = std::make_pair(anAttributeToBeModified,
537                                                               aCoincidentPoint);
538     }
539   }
540 }
541
542 void SketchPlugin_ConstraintSplit::getRefAttributes(const FeaturePtr& theFeature,
543                                     std::map<AttributePtr, std::list<AttributePtr> >& theRefs,
544                                     std::list<AttributePtr>& theRefsToFeature)
545 {
546   theRefs.clear();
547
548   std::list<AttributePtr> aPointAttributes = 
549     theFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
550   std::set<AttributePtr> aPointAttributesSet;
551
552   std::list<AttributePtr>::const_iterator aPIt = 
553     aPointAttributes.begin(), aPLast = aPointAttributes.end();
554   for (; aPIt != aPLast; aPIt++)
555     aPointAttributesSet.insert(*aPIt);
556
557   std::set<AttributePtr> aRefsAttributes = getFeatureResult(theFeature)->data()->refsToMe();
558   std::set<AttributePtr> aFRefsList = theFeature->data()->refsToMe();
559   aRefsAttributes.insert(aFRefsList.begin(), aFRefsList.end());
560
561   std::set<AttributePtr>::const_iterator aIt;
562   for (aIt = aRefsAttributes.cbegin(); aIt != aRefsAttributes.cend(); ++aIt) {
563     AttributePtr anAttr = (*aIt);
564     FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttr->owner());
565     if (anAttrFeature.get() != this &&
566         anAttr.get() && anAttr->attributeType() == ModelAPI_AttributeRefAttr::typeId()) {
567       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttr);
568       if (!aRefAttr->isObject()) { /// find attributes referenced to feature point attributes
569         AttributePtr anAttrInRef = aRefAttr->attr();
570         if (anAttrInRef.get() && 
571             aPointAttributesSet.find(anAttrInRef) != aPointAttributesSet.end()) {
572           if (theRefs.find(anAttrInRef) != theRefs.end())
573             theRefs[anAttrInRef].push_back(aRefAttr);
574           else {
575             std::list<AttributePtr> anAttrList;
576             anAttrList.push_back(aRefAttr);
577             theRefs[anAttrInRef] = anAttrList;
578           }
579         }
580       }
581       else { /// find attributes referenced to feature itself
582         theRefsToFeature.push_back(anAttr);
583       }
584     }
585   }
586 }
587
588 void SketchPlugin_ConstraintSplit::updateCoincidenceConstraintsToFeature(
589       const std::map<std::shared_ptr<ModelAPI_Feature>, IdToPointPair>& theCoincidenceToFeature,
590       const std::set<std::shared_ptr<GeomDataAPI_Point2D> >& theFurtherCoincidences,
591       const std::set<ResultPtr>& theFeatureResults)
592 {
593   if (theCoincidenceToFeature.empty())
594     return;
595
596   std::map<FeaturePtr, IdToPointPair>::const_iterator aCIt = theCoincidenceToFeature.begin(),
597                                                             aCLast = theCoincidenceToFeature.end();
598 #ifdef DEBUG_SPLIT
599   std::cout << std::endl;
600   std::cout << "Coincidences to feature(modified):"<< std::endl;
601 #endif
602   for (; aCIt != aCLast; aCIt++) {
603     FeaturePtr aCoincFeature = aCIt->first;
604     std::string anAttributeId = aCIt->second.first;
605     AttributePoint2DPtr aCoincPoint = aCIt->second.second;
606     std::set<AttributePoint2DPtr>::const_iterator aFCIt = theFurtherCoincidences.begin(),
607                                                   aFCLast = theFurtherCoincidences.end();
608     std::shared_ptr<GeomAPI_Pnt2d> aCoincPnt = aCoincPoint->pnt();
609     AttributePoint2DPtr aFeaturePointAttribute;
610     for (; aFCIt != aFCLast && !aFeaturePointAttribute.get(); aFCIt++) {
611       AttributePoint2DPtr aFCAttribute = *aFCIt;
612       if (aCoincPnt->isEqual(aFCAttribute->pnt()))
613         aFeaturePointAttribute = aFCAttribute;
614     }
615     if (aFeaturePointAttribute.get()) {
616       aCoincFeature->refattr(anAttributeId)->setObject(ResultPtr());
617       aCoincFeature->refattr(anAttributeId)->setAttr(aFeaturePointAttribute);
618     }
619     else {
620       /// find feature by shape intersected the point
621       ResultPtr aResultForCoincidence = *(theFeatureResults.begin());
622
623       if (theFeatureResults.size() > 1) { // try to find point on additional feature
624         ResultPtr anAddtionalResult = *(theFeatureResults.begin()++);
625         GeomShapePtr aShape = anAddtionalResult->shape();
626
627         std::shared_ptr<GeomAPI_Pnt2d> aPnt2d = aCoincPoint->pnt();
628         std::shared_ptr<GeomAPI_Pnt> aPoint = sketch()->to3D(aPnt2d->x(), aPnt2d->y());
629
630         std::shared_ptr<GeomAPI_Pnt> aProjectedPoint;
631         if (ModelGeomAlgo_Point2D::isPointOnEdge(aShape, aPoint, aProjectedPoint))
632           aResultForCoincidence = anAddtionalResult;
633       }
634       aCoincFeature->refattr(anAttributeId)->setObject(aResultForCoincidence);
635     }
636 #ifdef DEBUG_SPLIT
637   std::cout << " -" << getFeatureInfo(aCoincFeature) << std::endl;
638 #endif
639   }
640 }
641
642 void SketchPlugin_ConstraintSplit::updateTangentConstraintsToFeature(
643       const std::map<std::shared_ptr<ModelAPI_Feature>, IdToPointPair>& theTangentFeatures,
644       const std::set<std::shared_ptr<GeomDataAPI_Point2D> >& theFurtherCoincidences)
645 {
646   if (theTangentFeatures.empty())
647     return;
648
649   std::map<FeaturePtr, IdToPointPair>::const_iterator aTIt = theTangentFeatures.begin(),
650                                                       aTLast = theTangentFeatures.end();
651 #ifdef DEBUG_SPLIT
652   std::cout << std::endl;
653   std::cout << "Tangencies to feature(modified):"<< std::endl;
654 #endif
655   for (; aTIt != aTLast; aTIt++) {
656     FeaturePtr aTangentFeature = aTIt->first;
657     std::string anAttributeId = aTIt->second.first;
658     AttributePoint2DPtr aTangentPoint = aTIt->second.second;
659     std::set<AttributePoint2DPtr>::const_iterator aFCIt = theFurtherCoincidences.begin(),
660                                                   aFCLast = theFurtherCoincidences.end();
661     std::shared_ptr<GeomAPI_Pnt2d> aCoincPnt = aTangentPoint->pnt();
662     AttributePoint2DPtr aFeaturePointAttribute;
663     /// here we rely on created coincidence between further coincidence point and tangent result
664     for (; aFCIt != aFCLast && !aFeaturePointAttribute.get(); aFCIt++) {
665       AttributePoint2DPtr aFCAttribute = *aFCIt;
666       if (aCoincPnt->isEqual(aFCAttribute->pnt()))
667         aFeaturePointAttribute = aFCAttribute;
668     }
669     if (aFeaturePointAttribute.get()) {
670       FeaturePtr aFeature = 
671         std::dynamic_pointer_cast<ModelAPI_Feature>(aFeaturePointAttribute->owner());
672       aTangentFeature->refattr(anAttributeId)->setObject(getFeatureResult(aFeature));
673     }
674 #ifdef DEBUG_SPLIT
675   std::cout << " -" << getFeatureInfo(aTangentFeature) << std::endl;
676 #endif
677   }
678 }
679
680 void SketchPlugin_ConstraintSplit::updateRefFeatureConstraints(
681                                                   const ResultPtr& theFeatureBaseResult,
682                                                   const std::list<AttributePtr>& theRefsToFeature)
683 {
684   std::list<AttributePtr>::const_iterator anIt = theRefsToFeature.begin(),
685                                           aLast = theRefsToFeature.end();
686   for (; anIt != aLast; anIt++) {
687     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*anIt);
688     if (aRefAttr.get())
689       aRefAttr->setObject(theFeatureBaseResult);
690   }
691 }
692
693 void SketchPlugin_ConstraintSplit::updateRefAttConstraints(
694                     const std::map<AttributePtr, std::list<AttributePtr> >& theBaseRefAttributes,
695                     const std::set<std::pair<AttributePtr, AttributePtr> >& theModifiedAttributes)
696 {
697 #ifdef DEBUG_SPLIT
698   std::cout << "SketchPlugin_ConstraintSplit::updateRefAttConstraints" << std::endl;
699 #endif
700
701   std::set<std::pair<AttributePtr, AttributePtr> >::const_iterator 
702     anIt = theModifiedAttributes.begin(),  aLast = theModifiedAttributes.end();
703   for (; anIt != aLast; anIt++) {
704     AttributePtr anAttribute = anIt->first;
705
706     /// not found in references
707     if (theBaseRefAttributes.find(anAttribute) == theBaseRefAttributes.end()) 
708       continue;
709     std::list<AttributePtr> aRefAttributes = theBaseRefAttributes.at(anAttribute);
710     std::list<AttributePtr>::const_iterator aRefIt = aRefAttributes.begin(),
711                                             aRLast = aRefAttributes.end();
712
713     AttributePtr aNewAttribute = anIt->second;
714     for (; aRefIt != aRLast; aRefIt++) {
715       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*aRefIt);
716       if (aRefAttr.get()) {
717         aRefAttr->setAttr(aNewAttribute);
718 #ifdef DEBUG_SPLIT
719         FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->owner());
720         std::cout << " -" << getFeatureInfo(aFeature) << std::endl;
721 #endif
722       }
723     }
724   }
725 }
726
727 void SketchPlugin_ConstraintSplit::splitLine(FeaturePtr& theSplitFeature,
728                                              FeaturePtr& theBaseFeatureModified,
729                                              FeaturePtr& theAfterFeature,
730                                              std::set<AttributePoint2DPtr>& thePoints,
731                                              std::set<FeaturePtr>& theCreatedFeatures,
732                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
733 {
734   std::set<FeaturePtr> aCreatedFeatures;
735   FeaturePtr aConstraintFeature;
736   theBaseFeatureModified = FeaturePtr(); // it will contain modified base feature
737
738   SketchPlugin_Sketch* aSketch = sketch();
739   if (!aSketch)
740     return;
741
742   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
743                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
744   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
745   std::string aFeatureKind = aBaseFeature->getKind();
746   if (aFeatureKind != SketchPlugin_Line::ID())
747     return;
748
749   AttributePoint2DPtr aFirstPointAttrOfSplit = 
750     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
751   AttributePoint2DPtr aSecondPointAttrOfSplit = 
752     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
753   AttributePoint2DPtr aStartPointAttrOfBase, anEndPointAttrOfBase;
754   getFeaturePoints(aStartPointAttrOfBase, anEndPointAttrOfBase);
755   if (!aStartPointAttrOfBase.get() && !anEndPointAttrOfBase.get()) {
756     setError("Error: Feature has no start and end points.");
757     return;
758   }
759
760   arrangePointsOnLine(aStartPointAttrOfBase, anEndPointAttrOfBase, 
761                       aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
762
763 #ifdef DEBUG_SPLIT
764   std::cout << "Arranged points (to build split between 1st and 2nd points:" << std::endl;
765   std::cout << "Start point: " << 
766     ModelGeomAlgo_Point2D::getPointAttributeInfo(aStartPointAttrOfBase) << std::endl;
767   std::cout << "1st point:   " << 
768     ModelGeomAlgo_Point2D::getPointAttributeInfo(aFirstPointAttrOfSplit) << std::endl;
769   std::cout << "2nd point:   " << 
770     ModelGeomAlgo_Point2D::getPointAttributeInfo(aSecondPointAttrOfSplit) << std::endl;
771   std::cout << "End point:   " << 
772     ModelGeomAlgo_Point2D::getPointAttributeInfo(anEndPointAttrOfBase) << std::endl;
773 #endif
774
775   /// create a split feature
776   theSplitFeature = 
777     createLineFeature(aBaseFeature, aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
778   theCreatedFeatures.insert(theSplitFeature);
779
780   // before split feature
781   if (aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
782     theModifiedAttributes.insert(std::make_pair(aStartPointAttrOfBase,
783                                         theSplitFeature->attribute(SketchPlugin_Line::START_ID())));
784   }
785   else {
786     theBaseFeatureModified = aBaseFeature; ///< use base feature to store all constraints here
787     /// move end arc point to start of split
788   }
789
790   // after split feature
791   if (!aSecondPointAttrOfSplit->pnt()->isEqual(anEndPointAttrOfBase->pnt())) {
792     FeaturePtr aFeature;
793     if (!theBaseFeatureModified.get()) {
794       aFeature = aBaseFeature; ///< use base feature to store all constraints here
795       fillAttribute(aFeature->attribute(SketchPlugin_Line::START_ID()), aSecondPointAttrOfSplit);
796       aFeature->execute(); // to update result
797     }
798     else {
799       aFeature = createLineFeature(aBaseFeature, aSecondPointAttrOfSplit, anEndPointAttrOfBase);
800       theCreatedFeatures.insert(aFeature);
801       theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
802                                              aFeature->attribute(SketchPlugin_Line::END_ID())));
803     }
804     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
805                      theSplitFeature->attribute(SketchPlugin_Line::END_ID()),
806                      aFeature->attribute(SketchPlugin_Line::START_ID()));
807     theCreatedFeatures.insert(aConstraintFeature);
808
809     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
810                                 (aFeature->attribute(SketchPlugin_Line::START_ID())));
811     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
812                                 (aFeature->attribute(SketchPlugin_Line::END_ID())));
813
814     if (!theBaseFeatureModified.get())
815       theBaseFeatureModified = aFeature;
816     else
817       theAfterFeature = aFeature;
818   }
819   else {
820     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
821                                   (theSplitFeature->attribute(SketchPlugin_Line::END_ID())));
822     theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
823                                    theSplitFeature->attribute(SketchPlugin_Line::END_ID())));
824   }
825   // base split, that is defined before split feature should be changed at end
826   // (after the after feature creation). Otherwise modified value will be used in after feature
827   // before split feature
828   if (!aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
829     /// move end arc point to start of split
830     fillAttribute(theBaseFeatureModified->attribute(SketchPlugin_Line::END_ID()), 
831                                                     aFirstPointAttrOfSplit);
832     theBaseFeatureModified->execute(); // to update result
833     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
834                      theBaseFeatureModified->attribute(SketchPlugin_Line::END_ID()),
835                      theSplitFeature->attribute(SketchPlugin_Line::START_ID()));
836     theCreatedFeatures.insert(aConstraintFeature);
837
838     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
839                              (theBaseFeatureModified->attribute(SketchPlugin_Line::START_ID())));
840     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
841                                (theBaseFeatureModified->attribute(SketchPlugin_Line::END_ID())));
842   }
843   else
844     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
845                                        (theSplitFeature->attribute(SketchPlugin_Line::START_ID())));
846
847   // additional constraints between split and base features
848   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintParallel::ID(),
849                                                        getFeatureResult(aBaseFeature),
850                                                        getFeatureResult(theSplitFeature));
851   theCreatedFeatures.insert(aConstraintFeature);
852   if (theAfterFeature.get()) {
853     aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintParallel::ID(),
854                                                     getFeatureResult(aBaseFeature),
855                                                     getFeatureResult(theAfterFeature));
856     theCreatedFeatures.insert(aConstraintFeature);
857   }
858 }
859
860 void SketchPlugin_ConstraintSplit::splitArc(FeaturePtr& theSplitFeature,
861                                             FeaturePtr& theBaseFeatureModified,
862                                             FeaturePtr& theAfterFeature,
863                                             std::set<AttributePoint2DPtr>& thePoints,
864                                             std::set<FeaturePtr>& theCreatedFeatures,
865                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
866 {
867   std::set<FeaturePtr> aCreatedFeatures;
868   FeaturePtr aConstraintFeature;
869   theBaseFeatureModified = FeaturePtr(); // it will contain modified base feature
870
871   SketchPlugin_Sketch* aSketch = sketch();
872   if (!aSketch)
873     return;
874
875   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
876                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
877   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
878   std::string aFeatureKind = aBaseFeature->getKind();
879   if (aFeatureKind != SketchPlugin_Arc::ID())
880     return;
881
882   AttributePoint2DPtr aFirstPointAttrOfSplit = 
883     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
884   AttributePoint2DPtr aSecondPointAttrOfSplit = 
885     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
886   AttributePoint2DPtr aStartPointAttrOfBase, anEndPointAttrOfBase;
887   getFeaturePoints(aStartPointAttrOfBase, anEndPointAttrOfBase);
888   if (!aStartPointAttrOfBase.get() && !anEndPointAttrOfBase.get()) {
889     setError("Error: Feature has no start and end points.");
890     return;
891   }
892
893   // manually change type of arc to avoid incorrect self-constrainting of the tangent arc
894   aBaseFeature->string(SketchPlugin_Arc::ARC_TYPE())->setValue(
895       SketchPlugin_Arc::ARC_TYPE_CENTER_START_END());
896
897   arrangePointsOnArc(aBaseFeature, aStartPointAttrOfBase, anEndPointAttrOfBase,
898                      aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
899 #ifdef DEBUG_SPLIT
900   std::cout << "Arranged points (to build split between 1st and 2nd points:" << std::endl;
901   std::cout << "Start point: " << 
902     ModelGeomAlgo_Point2D::getPointAttributeInfo(aStartPointAttrOfBase) << std::endl;
903   std::cout << "1st point:   " << 
904     ModelGeomAlgo_Point2D::getPointAttributeInfo(aFirstPointAttrOfSplit) << std::endl;
905   std::cout << "2nd point:   " << 
906     ModelGeomAlgo_Point2D::getPointAttributeInfo(aSecondPointAttrOfSplit) << std::endl;
907   std::cout << "End point:   " << 
908     ModelGeomAlgo_Point2D::getPointAttributeInfo(anEndPointAttrOfBase) << std::endl;
909 #endif
910
911   /// split feature
912   theSplitFeature = createArcFeature(aBaseFeature, aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
913   theCreatedFeatures.insert(theSplitFeature);
914
915   // before split feature
916   if (aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
917     theModifiedAttributes.insert(std::make_pair(aStartPointAttrOfBase,
918                                   theSplitFeature->attribute(SketchPlugin_Arc::START_ID())));
919   }
920   else {
921     theBaseFeatureModified = aBaseFeature; ///< use base feature to store all constraints here
922     /// move end arc point to start of split
923   }
924
925   // after split feature
926   if (!aSecondPointAttrOfSplit->pnt()->isEqual(anEndPointAttrOfBase->pnt())) {
927     FeaturePtr aFeature;
928     if (!theBaseFeatureModified.get()) {
929       aFeature = aBaseFeature; ///< use base feature to store all constraints here
930       fillAttribute(aFeature->attribute(SketchPlugin_Arc::START_ID()), aSecondPointAttrOfSplit);
931       aFeature->execute(); // to update result
932     }
933     else {
934       aFeature = createArcFeature(aBaseFeature, aSecondPointAttrOfSplit, anEndPointAttrOfBase);
935       theCreatedFeatures.insert(aFeature);
936       theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
937                                                   aFeature->attribute(SketchPlugin_Arc::END_ID())));
938     }
939     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
940                      theSplitFeature->attribute(SketchPlugin_Arc::END_ID()),
941                      aFeature->attribute(SketchPlugin_Arc::START_ID()));
942     theCreatedFeatures.insert(aConstraintFeature);
943
944     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
945                                 (aFeature->attribute(SketchPlugin_Arc::START_ID())));
946     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
947                                 (aFeature->attribute(SketchPlugin_Arc::END_ID())));
948
949     if (!theBaseFeatureModified.get())
950       theBaseFeatureModified = aFeature;
951     else
952       theAfterFeature = aFeature;
953   }
954   else {
955     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
956                                   (theSplitFeature->attribute(SketchPlugin_Arc::END_ID())));
957     theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
958                                    theSplitFeature->attribute(SketchPlugin_Arc::END_ID())));
959   }
960   // base split, that is defined before split feature should be changed at end
961   // (after the after feature creation). Otherwise modified value will be used in after feature
962   // before split feature
963   if (!aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
964     /// move end arc point to start of split
965     fillAttribute(theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID()),
966                                                     aFirstPointAttrOfSplit);
967     theBaseFeatureModified->execute(); // to update result
968     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
969                      theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID()),
970                      theSplitFeature->attribute(SketchPlugin_Arc::START_ID()));
971     theCreatedFeatures.insert(aConstraintFeature);
972
973     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
974                              (theBaseFeatureModified->attribute(SketchPlugin_Arc::START_ID())));
975     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
976                                (theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID())));
977   }
978   else
979     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
980                                        (theSplitFeature->attribute(SketchPlugin_Arc::START_ID())));
981
982   // additional constraints between split and base features
983   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintEqual::ID(),
984                                                        getFeatureResult(aBaseFeature),
985                                                        getFeatureResult(theSplitFeature));
986   theCreatedFeatures.insert(aConstraintFeature);
987   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintTangent::ID(),
988                                                        getFeatureResult(theSplitFeature),
989                                                        getFeatureResult(aBaseFeature));
990   theCreatedFeatures.insert(aConstraintFeature);
991   if (theAfterFeature.get()) {
992     aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintEqual::ID(),
993                                                     getFeatureResult(aBaseFeature),
994                                                     getFeatureResult(theAfterFeature));
995     theCreatedFeatures.insert(aConstraintFeature);
996     aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintTangent::ID(),
997                                                     getFeatureResult(theSplitFeature),
998                                                     getFeatureResult(theAfterFeature));
999     theCreatedFeatures.insert(aConstraintFeature);
1000   }
1001 }
1002
1003 void SketchPlugin_ConstraintSplit::splitCircle(FeaturePtr& theSplitFeature,
1004                                                FeaturePtr& theBaseFeatureModified,
1005                                                FeaturePtr& theAfterFeature,
1006                                                std::set<AttributePoint2DPtr>& thePoints,
1007                                                std::set<FeaturePtr>& theCreatedFeatures,
1008                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
1009 {
1010   std::set<FeaturePtr> aCreatedFeatures;
1011   FeaturePtr aConstraintFeature;
1012   theBaseFeatureModified = FeaturePtr(); // it will contain modified base feature
1013
1014   SketchPlugin_Sketch* aSketch = sketch();
1015   if (!aSketch)
1016     return;
1017
1018   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
1019                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
1020   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
1021   std::string aFeatureKind = aBaseFeature->getKind();
1022   if (aFeatureKind != SketchPlugin_Circle::ID())
1023     return;
1024
1025   AttributePoint2DPtr aFirstPointAttrOfSplit = 
1026     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
1027   AttributePoint2DPtr aSecondPointAttrOfSplit = 
1028     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
1029
1030   /// split feature
1031   theSplitFeature = 
1032     createArcFeature(aBaseFeature, aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
1033   bool aSplitReversed = std::dynamic_pointer_cast<SketchPlugin_Arc>(theSplitFeature)->isReversed();
1034   theCreatedFeatures.insert(theSplitFeature);
1035
1036   /// base feature is a left part of the circle
1037   theBaseFeatureModified = createArcFeature(aBaseFeature, 
1038     aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
1039   std::dynamic_pointer_cast<SketchPlugin_Arc>(
1040     theBaseFeatureModified)->setReversed(!aSplitReversed);
1041   theBaseFeatureModified->execute();
1042
1043   theModifiedAttributes.insert(
1044     std::make_pair(aBaseFeature->attribute(SketchPlugin_Circle::CENTER_ID()),
1045                   theBaseFeatureModified->attribute(SketchPlugin_Arc::CENTER_ID())));
1046
1047   theCreatedFeatures.insert(theBaseFeatureModified);
1048
1049   thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
1050                              (theBaseFeatureModified->attribute(SketchPlugin_Arc::START_ID())));
1051   thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
1052                              (theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID())));
1053
1054   // additional constraints between split and base features
1055   aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
1056                      theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID()),
1057                      theSplitFeature->attribute(SketchPlugin_Arc::END_ID()));
1058   theCreatedFeatures.insert(aConstraintFeature);
1059   aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
1060                      theBaseFeatureModified->attribute(SketchPlugin_Arc::START_ID()),
1061                      theSplitFeature->attribute(SketchPlugin_Arc::START_ID()));
1062   theCreatedFeatures.insert(aConstraintFeature);
1063
1064   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintTangent::ID(),
1065                                                        getFeatureResult(theSplitFeature),
1066                                                        getFeatureResult(theBaseFeatureModified));
1067   theCreatedFeatures.insert(aConstraintFeature);
1068 }
1069
1070 void SketchPlugin_ConstraintSplit::arrangePointsOnLine(
1071     const AttributePoint2DPtr& theStartPointAttr,
1072     const AttributePoint2DPtr& theEndPointAttr,
1073     AttributePoint2DPtr& theFirstPointAttr,
1074     AttributePoint2DPtr& theLastPointAttr) const
1075 {
1076   // if first point is closer to last point, swap first and last values
1077   if (theStartPointAttr->pnt()->distance(theFirstPointAttr->pnt()) >
1078       theStartPointAttr->pnt()->distance(theLastPointAttr->pnt())) {
1079     AttributePoint2DPtr aTmpPoint = theFirstPointAttr;
1080     theFirstPointAttr = theLastPointAttr;
1081     theLastPointAttr = aTmpPoint;
1082   }
1083 }
1084
1085 void SketchPlugin_ConstraintSplit::arrangePointsOnArc(
1086     const FeaturePtr& theArc,
1087     const std::shared_ptr<GeomDataAPI_Point2D>& theStartPointAttr,
1088     const std::shared_ptr<GeomDataAPI_Point2D>& theEndPointAttr,
1089     std::shared_ptr<GeomDataAPI_Point2D>& theFirstPointAttr,
1090     std::shared_ptr<GeomDataAPI_Point2D>& theSecondPointAttr) const
1091 {
1092   static const double anAngleTol = 1.e-12;
1093
1094   std::shared_ptr<GeomAPI_Pnt2d> aCenter = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1095       theArc->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
1096   bool isReversed = theArc->boolean(SketchPlugin_Arc::INVERSED_ID())->value();
1097
1098   // collect directions to each point
1099   std::shared_ptr<GeomAPI_Dir2d> aStartDir(
1100       new GeomAPI_Dir2d(theStartPointAttr->pnt()->xy()->decreased(aCenter->xy())));
1101   std::shared_ptr<GeomAPI_Dir2d> aFirstPtDir(
1102       new GeomAPI_Dir2d(theFirstPointAttr->pnt()->xy()->decreased(aCenter->xy())));
1103   std::shared_ptr<GeomAPI_Dir2d> aSecondPtDir(
1104       new GeomAPI_Dir2d(theSecondPointAttr->pnt()->xy()->decreased(aCenter->xy())));
1105
1106   // sort points by their angular values
1107   double aFirstPtAngle = aStartDir->angle(aFirstPtDir);
1108   double aSecondPtAngle = aStartDir->angle(aSecondPtDir);
1109   double aPeriod = isReversed ? -2.0 * PI : 2.0 * PI;
1110   if (fabs(aFirstPtAngle) > anAngleTol && isReversed == (aFirstPtAngle > 0.))
1111     aFirstPtAngle += aPeriod;
1112   if (fabs(aSecondPtAngle) > anAngleTol && isReversed == (aSecondPtAngle > 0.))
1113     aSecondPtAngle += aPeriod;
1114
1115   if (fabs(aFirstPtAngle) > fabs(aSecondPtAngle)) {
1116     AttributePoint2DPtr aTmpPoint = theFirstPointAttr;
1117     theFirstPointAttr = theSecondPointAttr;
1118     theSecondPointAttr = aTmpPoint;
1119   }
1120 }
1121
1122 void SketchPlugin_ConstraintSplit::fillAttribute(const AttributePtr& theModifiedAttribute,
1123                                                  const AttributePtr& theSourceAttribute)
1124 {
1125   AttributePoint2DPtr aModifiedAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1126                                             theModifiedAttribute);
1127   AttributePoint2DPtr aSourceAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1128                                             theSourceAttribute);
1129
1130   if (aModifiedAttribute.get() && aSourceAttribute.get())
1131     aModifiedAttribute->setValue(aSourceAttribute->pnt());
1132 }
1133
1134 FeaturePtr SketchPlugin_ConstraintSplit::createLineFeature(const FeaturePtr& theBaseFeature,
1135                                                            const AttributePtr& theFirstPointAttr,
1136                                                            const AttributePtr& theSecondPointAttr)
1137 {
1138   FeaturePtr aFeature;
1139   SketchPlugin_Sketch* aSketch = sketch();
1140   if (!aSketch || !theBaseFeature.get())
1141     return aFeature;
1142
1143   aFeature = aSketch->addFeature(SketchPlugin_Line::ID());
1144
1145   fillAttribute(aFeature->attribute(SketchPlugin_Line::START_ID()), theFirstPointAttr);
1146   fillAttribute(aFeature->attribute(SketchPlugin_Line::END_ID()), theSecondPointAttr);
1147   aFeature->execute(); // to obtain result
1148
1149   return aFeature;
1150 }
1151
1152 FeaturePtr SketchPlugin_ConstraintSplit::createArcFeature(const FeaturePtr& theBaseFeature,
1153                                                           const AttributePtr& theFirstPointAttr,
1154                                                           const AttributePtr& theSecondPointAttr)
1155 {
1156   FeaturePtr aFeature;
1157   SketchPlugin_Sketch* aSketch = sketch();
1158   if (!aSketch || !theBaseFeature.get())
1159     return aFeature;
1160
1161   std::string aCenterAttributeId;
1162   if (theBaseFeature->getKind() == SketchPlugin_Arc::ID())
1163     aCenterAttributeId = SketchPlugin_Arc::CENTER_ID();
1164   else if (theBaseFeature->getKind() == SketchPlugin_Circle::ID())
1165     aCenterAttributeId = SketchPlugin_Circle::CENTER_ID();
1166
1167   if (aCenterAttributeId.empty())
1168     return aFeature;
1169
1170   aFeature = aSketch->addFeature(SketchPlugin_Arc::ID());
1171   // update fillet arc: make the arc correct for sure, so, it is not needed to process 
1172   // the "attribute updated"
1173   // by arc; moreover, it may cause cyclicity in hte mechanism of updater
1174   aFeature->data()->blockSendAttributeUpdated(true);
1175
1176   aFeature->string(SketchPlugin_Arc::ARC_TYPE())->setValue(
1177                 SketchPlugin_Arc::ARC_TYPE_CENTER_START_END());
1178
1179   fillAttribute(aFeature->attribute(SketchPlugin_Arc::CENTER_ID()),
1180                 theBaseFeature->attribute(aCenterAttributeId));
1181   fillAttribute(aFeature->attribute(SketchPlugin_Arc::START_ID()), theFirstPointAttr);
1182   fillAttribute(aFeature->attribute(SketchPlugin_Arc::END_ID()), theSecondPointAttr);
1183
1184   /// fill referersed state of created arc as it is on the base arc
1185   if (theBaseFeature->getKind() == SketchPlugin_Arc::ID()) {
1186     bool aReversed = theBaseFeature->boolean(SketchPlugin_Arc::INVERSED_ID())->value();
1187     aFeature->boolean(SketchPlugin_Arc::INVERSED_ID())->setValue(aReversed);
1188   }
1189   aFeature->data()->blockSendAttributeUpdated(false);
1190   aFeature->execute(); // to obtain result
1191
1192   return aFeature;
1193 }
1194
1195 FeaturePtr SketchPlugin_ConstraintSplit::createConstraint(const std::string& theConstraintId,
1196                                                     const AttributePtr& theFirstAttribute,
1197                                                     const AttributePtr& theSecondAttribute)
1198 {
1199   FeaturePtr aConstraint = sketch()->addFeature(theConstraintId);
1200   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1201                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
1202   aRefAttr->setAttr(theFirstAttribute);
1203
1204   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1205                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
1206   aRefAttr->setAttr(theSecondAttribute);
1207
1208   return aConstraint;
1209 }
1210
1211 FeaturePtr SketchPlugin_ConstraintSplit::createConstraintForObjects(
1212                                                     const std::string& theConstraintId,
1213                                                     const ObjectPtr& theFirstObject,
1214                                                     const ObjectPtr& theSecondObject)
1215 {
1216   FeaturePtr aConstraint = sketch()->addFeature(theConstraintId);
1217   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1218                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
1219   aRefAttr->setObject(theFirstObject);
1220
1221   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1222                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
1223   aRefAttr->setObject(theSecondObject);
1224
1225   return aConstraint;
1226 }
1227
1228 void SketchPlugin_ConstraintSplit::updateFeaturesAfterSplit(
1229                                                    const std::set<FeaturePtr>& theFeaturesToUpdate)
1230 {
1231   std::set<FeaturePtr>::const_iterator anIt = theFeaturesToUpdate.begin(),
1232                                        aLast = theFeaturesToUpdate.end();
1233   for (; anIt != aLast; anIt++) {
1234     FeaturePtr aRefFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
1235     std::string aRefFeatureKind = aRefFeature->getKind();
1236     if (aRefFeatureKind == SketchPlugin_ConstraintLength::ID()) {
1237       std::shared_ptr<SketchPlugin_ConstraintLength> aLenghtFeature =
1238                               std::dynamic_pointer_cast<SketchPlugin_ConstraintLength>(*anIt);
1239       if (aLenghtFeature.get()) {
1240         std::shared_ptr<ModelAPI_AttributeDouble> aValueAttr = std::dynamic_pointer_cast<
1241             ModelAPI_AttributeDouble>(aLenghtFeature->attribute(SketchPlugin_Constraint::VALUE()));
1242         double aValue;
1243         if (aLenghtFeature->computeLenghtValue(aValue) && aValueAttr.get())
1244           aValueAttr->setValue(aValue);
1245       }
1246     }
1247   }
1248 }
1249
1250 std::shared_ptr<ModelAPI_Result> SketchPlugin_ConstraintSplit::getFeatureResult(
1251                                     const std::shared_ptr<ModelAPI_Feature>& theFeature)
1252 {
1253   std::shared_ptr<ModelAPI_Result> aResult;
1254
1255   std::string aFeatureKind = theFeature->getKind();
1256   if (aFeatureKind == SketchPlugin_Line::ID())
1257     aResult = theFeature->firstResult();
1258   else if (aFeatureKind == SketchPlugin_Arc::ID())
1259     aResult = theFeature->lastResult();
1260   else if (aFeatureKind == SketchPlugin_Circle::ID())
1261     aResult = theFeature->lastResult();
1262
1263   return aResult;
1264 }
1265
1266 std::set<std::shared_ptr<ModelAPI_Attribute> > SketchPlugin_ConstraintSplit::getEdgeAttributes(
1267                                            const std::shared_ptr<ModelAPI_Feature>& theFeature)
1268 {
1269   std::set<std::shared_ptr<ModelAPI_Attribute> > anAttributes;
1270
1271   std::string aFeatureKind = theFeature->getKind();
1272   if (aFeatureKind == SketchPlugin_Line::ID()) {
1273     anAttributes.insert(theFeature->attribute(SketchPlugin_Line::START_ID()));
1274     anAttributes.insert(theFeature->attribute(SketchPlugin_Line::END_ID()));
1275   }
1276   else if (aFeatureKind == SketchPlugin_Arc::ID()) {
1277     anAttributes.insert(theFeature->attribute(SketchPlugin_Arc::START_ID()));
1278     anAttributes.insert(theFeature->attribute(SketchPlugin_Arc::END_ID()));
1279   }
1280   else if (aFeatureKind == SketchPlugin_Circle::ID()) {
1281   }
1282
1283   return anAttributes;
1284 }
1285
1286
1287 std::string SketchPlugin_ConstraintSplit::getFeatureInfo(
1288                                                const std::shared_ptr<ModelAPI_Feature>& theFeature,
1289                                                const bool isUseAttributesInfo)
1290 {
1291   std::string anInfo;
1292   if (!theFeature.get()) {
1293     return "none";
1294   }
1295
1296   if (theFeature->data()->isValid())
1297     anInfo.append(theFeature->data()->name().c_str());
1298
1299   if (isUseAttributesInfo) {
1300     std::string aPointsInfo = ModelGeomAlgo_Point2D::getPontAttributesInfo(theFeature,
1301                                                              getEdgeAttributes(theFeature));
1302     /// processing of feature with point 2d attributes, like line, arc, circle
1303     if (!aPointsInfo.empty()) { 
1304       anInfo += ": ";
1305       anInfo += "\n";
1306       anInfo += aPointsInfo;
1307     }
1308     else { /// process constraint coincidence, find points in ref attr attributes
1309       std::list<AttributePtr> anAttrs = theFeature->data()->attributes(
1310                                                        ModelAPI_AttributeRefAttr::typeId());
1311       std::list<AttributePtr>::const_iterator anIt = anAttrs.begin(), aLast = anAttrs.end();
1312       std::string anAttributesInfo;
1313       for(; anIt != aLast; anIt++) {
1314         if (!anAttributesInfo.empty()) {
1315           anAttributesInfo.append(", ");
1316           anAttributesInfo += "\n";
1317         }
1318         AttributePtr anAttr = *anIt;
1319         std::string aValue = "not defined";
1320         std::string aType = anAttr->attributeType();
1321         if (aType == ModelAPI_AttributeRefAttr::typeId()) {
1322           std::shared_ptr<ModelAPI_AttributeRefAttr> aRefAttr =
1323                              std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttr);
1324           if (aRefAttr.get()) {
1325             if (aRefAttr->isObject()) {
1326               FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1327               aValue = "<object:>" + getFeatureInfo(aFeature, false);
1328             }
1329             else {
1330               AttributePtr anAttribute = aRefAttr->attr();
1331               if (anAttribute.get()) {
1332                 FeaturePtr aFeature = ModelAPI_Feature::feature(anAttribute->owner());
1333                 aValue = "<attr:>" + ModelGeomAlgo_Point2D::getPointAttributeInfo(anAttribute) +
1334                          " [" + getFeatureInfo(aFeature, false) + "]";
1335               }
1336             }
1337           }
1338         }
1339         anAttributesInfo.append("    " + anAttr->id() + ": " + aValue);
1340       }
1341       if (!anAttributesInfo.empty())
1342         anInfo = anInfo + "\n" + anAttributesInfo;
1343     }
1344   }
1345   return anInfo;
1346 }
1347