Salome HOME
Issue #1834: Fix length of lines
[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()) {
536         if (isToFeature)
537           theCoincidenceToFeature[aRefFeature] = std::make_pair(anAttributeToBeModified,
538                                                                 aCoincidentPoint);
539       }
540       else
541         theFeaturesToDelete.insert(aRefFeature); /// this case should not happen
542     }
543   }
544 }
545
546 void SketchPlugin_ConstraintSplit::getRefAttributes(const FeaturePtr& theFeature,
547                                     std::map<AttributePtr, std::list<AttributePtr> >& theRefs,
548                                     std::list<AttributePtr>& theRefsToFeature)
549 {
550   theRefs.clear();
551
552   std::list<AttributePtr> aPointAttributes = 
553     theFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
554   std::set<AttributePtr> aPointAttributesSet;
555
556   std::list<AttributePtr>::const_iterator aPIt = 
557     aPointAttributes.begin(), aPLast = aPointAttributes.end();
558   for (; aPIt != aPLast; aPIt++)
559     aPointAttributesSet.insert(*aPIt);
560
561   std::set<AttributePtr> aRefsAttributes = getFeatureResult(theFeature)->data()->refsToMe();
562   std::set<AttributePtr> aFRefsList = theFeature->data()->refsToMe();
563   aRefsAttributes.insert(aFRefsList.begin(), aFRefsList.end());
564
565   std::set<AttributePtr>::const_iterator aIt;
566   for (aIt = aRefsAttributes.cbegin(); aIt != aRefsAttributes.cend(); ++aIt) {
567     AttributePtr anAttr = (*aIt);
568     FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttr->owner());
569     if (anAttrFeature.get() != this &&
570         anAttr.get() && anAttr->attributeType() == ModelAPI_AttributeRefAttr::typeId()) {
571       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttr);
572       if (!aRefAttr->isObject()) { /// find attributes referenced to feature point attributes
573         AttributePtr anAttrInRef = aRefAttr->attr();
574         if (anAttrInRef.get() && 
575             aPointAttributesSet.find(anAttrInRef) != aPointAttributesSet.end()) {
576           if (theRefs.find(anAttrInRef) != theRefs.end())
577             theRefs[anAttrInRef].push_back(aRefAttr);
578           else {
579             std::list<AttributePtr> anAttrList;
580             anAttrList.push_back(aRefAttr);
581             theRefs[anAttrInRef] = anAttrList;
582           }
583         }
584       }
585       else { /// find attributes referenced to feature itself
586         theRefsToFeature.push_back(anAttr);
587       }
588     }
589   }
590 }
591
592 void SketchPlugin_ConstraintSplit::updateCoincidenceConstraintsToFeature(
593       const std::map<std::shared_ptr<ModelAPI_Feature>, IdToPointPair>& theCoincidenceToFeature,
594       const std::set<std::shared_ptr<GeomDataAPI_Point2D> >& theFurtherCoincidences,
595       const std::set<ResultPtr>& theFeatureResults)
596 {
597   if (theCoincidenceToFeature.empty())
598     return;
599
600   std::map<FeaturePtr, IdToPointPair>::const_iterator aCIt = theCoincidenceToFeature.begin(),
601                                                             aCLast = theCoincidenceToFeature.end();
602 #ifdef DEBUG_SPLIT
603   std::cout << std::endl;
604   std::cout << "Coincidences to feature(modified):"<< std::endl;
605 #endif
606   for (; aCIt != aCLast; aCIt++) {
607     FeaturePtr aCoincFeature = aCIt->first;
608     std::string anAttributeId = aCIt->second.first;
609     AttributePoint2DPtr aCoincPoint = aCIt->second.second;
610     std::set<AttributePoint2DPtr>::const_iterator aFCIt = theFurtherCoincidences.begin(),
611                                                   aFCLast = theFurtherCoincidences.end();
612     std::shared_ptr<GeomAPI_Pnt2d> aCoincPnt = aCoincPoint->pnt();
613     AttributePoint2DPtr aFeaturePointAttribute;
614     for (; aFCIt != aFCLast && !aFeaturePointAttribute.get(); aFCIt++) {
615       AttributePoint2DPtr aFCAttribute = *aFCIt;
616       if (aCoincPnt->isEqual(aFCAttribute->pnt()))
617         aFeaturePointAttribute = aFCAttribute;
618     }
619     if (aFeaturePointAttribute.get()) {
620       aCoincFeature->refattr(anAttributeId)->setObject(ResultPtr());
621       aCoincFeature->refattr(anAttributeId)->setAttr(aFeaturePointAttribute);
622     }
623     else {
624       /// find feature by shape intersected the point
625       ResultPtr aResultForCoincidence = *(theFeatureResults.begin());
626
627       if (theFeatureResults.size() > 1) { // try to find point on additional feature
628         ResultPtr anAddtionalResult = *(theFeatureResults.begin()++);
629         GeomShapePtr aShape = anAddtionalResult->shape();
630
631         std::shared_ptr<GeomAPI_Pnt2d> aPnt2d = aCoincPoint->pnt();
632         std::shared_ptr<GeomAPI_Pnt> aPoint = sketch()->to3D(aPnt2d->x(), aPnt2d->y());
633
634         std::shared_ptr<GeomAPI_Pnt> aProjectedPoint;
635         if (ModelGeomAlgo_Point2D::isPointOnEdge(aShape, aPoint, aProjectedPoint))
636           aResultForCoincidence = anAddtionalResult;
637       }
638       aCoincFeature->refattr(anAttributeId)->setObject(aResultForCoincidence);
639     }
640 #ifdef DEBUG_SPLIT
641   std::cout << " -" << getFeatureInfo(aCoincFeature) << std::endl;
642 #endif
643   }
644 }
645
646 void SketchPlugin_ConstraintSplit::updateTangentConstraintsToFeature(
647       const std::map<std::shared_ptr<ModelAPI_Feature>, IdToPointPair>& theTangentFeatures,
648       const std::set<std::shared_ptr<GeomDataAPI_Point2D> >& theFurtherCoincidences)
649 {
650   if (theTangentFeatures.empty())
651     return;
652
653   std::map<FeaturePtr, IdToPointPair>::const_iterator aTIt = theTangentFeatures.begin(),
654                                                       aTLast = theTangentFeatures.end();
655 #ifdef DEBUG_SPLIT
656   std::cout << std::endl;
657   std::cout << "Tangencies to feature(modified):"<< std::endl;
658 #endif
659   for (; aTIt != aTLast; aTIt++) {
660     FeaturePtr aTangentFeature = aTIt->first;
661     std::string anAttributeId = aTIt->second.first;
662     AttributePoint2DPtr aTangentPoint = aTIt->second.second;
663     std::set<AttributePoint2DPtr>::const_iterator aFCIt = theFurtherCoincidences.begin(),
664                                                   aFCLast = theFurtherCoincidences.end();
665     std::shared_ptr<GeomAPI_Pnt2d> aCoincPnt = aTangentPoint->pnt();
666     AttributePoint2DPtr aFeaturePointAttribute;
667     /// here we rely on created coincidence between further coincidence point and tangent result
668     for (; aFCIt != aFCLast && !aFeaturePointAttribute.get(); aFCIt++) {
669       AttributePoint2DPtr aFCAttribute = *aFCIt;
670       if (aCoincPnt->isEqual(aFCAttribute->pnt()))
671         aFeaturePointAttribute = aFCAttribute;
672     }
673     if (aFeaturePointAttribute.get()) {
674       FeaturePtr aFeature = 
675         std::dynamic_pointer_cast<ModelAPI_Feature>(aFeaturePointAttribute->owner());
676       aTangentFeature->refattr(anAttributeId)->setObject(getFeatureResult(aFeature));
677     }
678 #ifdef DEBUG_SPLIT
679   std::cout << " -" << getFeatureInfo(aTangentFeature) << std::endl;
680 #endif
681   }
682 }
683
684 void SketchPlugin_ConstraintSplit::updateRefFeatureConstraints(
685                                                   const ResultPtr& theFeatureBaseResult,
686                                                   const std::list<AttributePtr>& theRefsToFeature)
687 {
688   std::list<AttributePtr>::const_iterator anIt = theRefsToFeature.begin(),
689                                           aLast = theRefsToFeature.end();
690   for (; anIt != aLast; anIt++) {
691     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*anIt);
692     if (aRefAttr.get())
693       aRefAttr->setObject(theFeatureBaseResult);
694   }
695 }
696
697 void SketchPlugin_ConstraintSplit::updateRefAttConstraints(
698                     const std::map<AttributePtr, std::list<AttributePtr> >& theBaseRefAttributes,
699                     const std::set<std::pair<AttributePtr, AttributePtr> >& theModifiedAttributes)
700 {
701 #ifdef DEBUG_SPLIT
702   std::cout << "SketchPlugin_ConstraintSplit::updateRefAttConstraints" << std::endl;
703 #endif
704
705   std::set<std::pair<AttributePtr, AttributePtr> >::const_iterator 
706     anIt = theModifiedAttributes.begin(),  aLast = theModifiedAttributes.end();
707   for (; anIt != aLast; anIt++) {
708     AttributePtr anAttribute = anIt->first;
709
710     /// not found in references
711     if (theBaseRefAttributes.find(anAttribute) == theBaseRefAttributes.end()) 
712       continue;
713     std::list<AttributePtr> aRefAttributes = theBaseRefAttributes.at(anAttribute);
714     std::list<AttributePtr>::const_iterator aRefIt = aRefAttributes.begin(),
715                                             aRLast = aRefAttributes.end();
716
717     AttributePtr aNewAttribute = anIt->second;
718     for (; aRefIt != aRLast; aRefIt++) {
719       AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*aRefIt);
720       if (aRefAttr.get()) {
721         aRefAttr->setAttr(aNewAttribute);
722 #ifdef DEBUG_SPLIT
723         FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->owner());
724         std::cout << " -" << getFeatureInfo(aFeature) << std::endl;
725 #endif
726       }
727     }
728   }
729 }
730
731 void SketchPlugin_ConstraintSplit::splitLine(FeaturePtr& theSplitFeature,
732                                              FeaturePtr& theBaseFeatureModified,
733                                              FeaturePtr& theAfterFeature,
734                                              std::set<AttributePoint2DPtr>& thePoints,
735                                              std::set<FeaturePtr>& theCreatedFeatures,
736                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
737 {
738   std::set<FeaturePtr> aCreatedFeatures;
739   FeaturePtr aConstraintFeature;
740   theBaseFeatureModified = FeaturePtr(); // it will contain modified base feature
741
742   SketchPlugin_Sketch* aSketch = sketch();
743   if (!aSketch)
744     return;
745
746   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
747                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
748   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
749   std::string aFeatureKind = aBaseFeature->getKind();
750   if (aFeatureKind != SketchPlugin_Line::ID())
751     return;
752
753   AttributePoint2DPtr aFirstPointAttrOfSplit = 
754     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
755   AttributePoint2DPtr aSecondPointAttrOfSplit = 
756     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
757   AttributePoint2DPtr aStartPointAttrOfBase, anEndPointAttrOfBase;
758   getFeaturePoints(aStartPointAttrOfBase, anEndPointAttrOfBase);
759   if (!aStartPointAttrOfBase.get() && !anEndPointAttrOfBase.get()) {
760     setError("Error: Feature has no start and end points.");
761     return;
762   }
763
764   arrangePointsOnLine(aStartPointAttrOfBase, anEndPointAttrOfBase, 
765                       aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
766
767 #ifdef DEBUG_SPLIT
768   std::cout << "Arranged points (to build split between 1st and 2nd points:" << std::endl;
769   std::cout << "Start point: " << 
770     ModelGeomAlgo_Point2D::getPointAttributeInfo(aStartPointAttrOfBase) << std::endl;
771   std::cout << "1st point:   " << 
772     ModelGeomAlgo_Point2D::getPointAttributeInfo(aFirstPointAttrOfSplit) << std::endl;
773   std::cout << "2nd point:   " << 
774     ModelGeomAlgo_Point2D::getPointAttributeInfo(aSecondPointAttrOfSplit) << std::endl;
775   std::cout << "End point:   " << 
776     ModelGeomAlgo_Point2D::getPointAttributeInfo(anEndPointAttrOfBase) << std::endl;
777 #endif
778
779   /// create a split feature
780   theSplitFeature = 
781     createLineFeature(aBaseFeature, aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
782   theCreatedFeatures.insert(theSplitFeature);
783
784   // before split feature
785   if (aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
786     theModifiedAttributes.insert(std::make_pair(aStartPointAttrOfBase,
787                                         theSplitFeature->attribute(SketchPlugin_Line::START_ID())));
788   }
789   else {
790     theBaseFeatureModified = aBaseFeature; ///< use base feature to store all constraints here
791     /// move end arc point to start of split
792   }
793
794   // after split feature
795   if (!aSecondPointAttrOfSplit->pnt()->isEqual(anEndPointAttrOfBase->pnt())) {
796     FeaturePtr aFeature;
797     if (!theBaseFeatureModified.get()) {
798       aFeature = aBaseFeature; ///< use base feature to store all constraints here
799       fillAttribute(aFeature->attribute(SketchPlugin_Line::START_ID()), aSecondPointAttrOfSplit);
800       aFeature->execute(); // to update result
801     }
802     else {
803       aFeature = createLineFeature(aBaseFeature, aSecondPointAttrOfSplit, anEndPointAttrOfBase);
804       theCreatedFeatures.insert(aFeature);
805       theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
806                                              aFeature->attribute(SketchPlugin_Line::END_ID())));
807     }
808     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
809                      theSplitFeature->attribute(SketchPlugin_Line::END_ID()),
810                      aFeature->attribute(SketchPlugin_Line::START_ID()));
811     theCreatedFeatures.insert(aConstraintFeature);
812
813     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
814                                 (aFeature->attribute(SketchPlugin_Line::START_ID())));
815     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
816                                 (aFeature->attribute(SketchPlugin_Line::END_ID())));
817
818     if (!theBaseFeatureModified.get())
819       theBaseFeatureModified = aFeature;
820     else
821       theAfterFeature = aFeature;
822   }
823   else {
824     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
825                                   (theSplitFeature->attribute(SketchPlugin_Line::END_ID())));
826     theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
827                                    theSplitFeature->attribute(SketchPlugin_Line::END_ID())));
828   }
829   // base split, that is defined before split feature should be changed at end
830   // (after the after feature creation). Otherwise modified value will be used in after feature
831   // before split feature
832   if (!aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
833     /// move end arc point to start of split
834     fillAttribute(theBaseFeatureModified->attribute(SketchPlugin_Line::END_ID()), 
835                                                     aFirstPointAttrOfSplit);
836     theBaseFeatureModified->execute(); // to update result
837     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
838                      theBaseFeatureModified->attribute(SketchPlugin_Line::END_ID()),
839                      theSplitFeature->attribute(SketchPlugin_Line::START_ID()));
840     theCreatedFeatures.insert(aConstraintFeature);
841
842     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
843                              (theBaseFeatureModified->attribute(SketchPlugin_Line::START_ID())));
844     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
845                                (theBaseFeatureModified->attribute(SketchPlugin_Line::END_ID())));
846   }
847   else
848     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
849                                        (theSplitFeature->attribute(SketchPlugin_Line::START_ID())));
850
851   // additional constraints between split and base features
852   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintParallel::ID(),
853                                                        getFeatureResult(aBaseFeature),
854                                                        getFeatureResult(theSplitFeature));
855   theCreatedFeatures.insert(aConstraintFeature);
856   if (theAfterFeature.get()) {
857     aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintParallel::ID(),
858                                                     getFeatureResult(aBaseFeature),
859                                                     getFeatureResult(theAfterFeature));
860     theCreatedFeatures.insert(aConstraintFeature);
861   }
862 }
863
864 void SketchPlugin_ConstraintSplit::splitArc(FeaturePtr& theSplitFeature,
865                                             FeaturePtr& theBaseFeatureModified,
866                                             FeaturePtr& theAfterFeature,
867                                             std::set<AttributePoint2DPtr>& thePoints,
868                                             std::set<FeaturePtr>& theCreatedFeatures,
869                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
870 {
871   std::set<FeaturePtr> aCreatedFeatures;
872   FeaturePtr aConstraintFeature;
873   theBaseFeatureModified = FeaturePtr(); // it will contain modified base feature
874
875   SketchPlugin_Sketch* aSketch = sketch();
876   if (!aSketch)
877     return;
878
879   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
880                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
881   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
882   std::string aFeatureKind = aBaseFeature->getKind();
883   if (aFeatureKind != SketchPlugin_Arc::ID())
884     return;
885
886   AttributePoint2DPtr aFirstPointAttrOfSplit = 
887     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
888   AttributePoint2DPtr aSecondPointAttrOfSplit = 
889     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
890   AttributePoint2DPtr aStartPointAttrOfBase, anEndPointAttrOfBase;
891   getFeaturePoints(aStartPointAttrOfBase, anEndPointAttrOfBase);
892   if (!aStartPointAttrOfBase.get() && !anEndPointAttrOfBase.get()) {
893     setError("Error: Feature has no start and end points.");
894     return;
895   }
896
897   // manually change type of arc to avoid incorrect self-constrainting of the tangent arc
898   aBaseFeature->string(SketchPlugin_Arc::ARC_TYPE())->setValue(
899       SketchPlugin_Arc::ARC_TYPE_CENTER_START_END());
900
901   arrangePointsOnArc(aBaseFeature, aStartPointAttrOfBase, anEndPointAttrOfBase,
902                      aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
903 #ifdef DEBUG_SPLIT
904   std::cout << "Arranged points (to build split between 1st and 2nd points:" << std::endl;
905   std::cout << "Start point: " << 
906     ModelGeomAlgo_Point2D::getPointAttributeInfo(aStartPointAttrOfBase) << std::endl;
907   std::cout << "1st point:   " << 
908     ModelGeomAlgo_Point2D::getPointAttributeInfo(aFirstPointAttrOfSplit) << std::endl;
909   std::cout << "2nd point:   " << 
910     ModelGeomAlgo_Point2D::getPointAttributeInfo(aSecondPointAttrOfSplit) << std::endl;
911   std::cout << "End point:   " << 
912     ModelGeomAlgo_Point2D::getPointAttributeInfo(anEndPointAttrOfBase) << std::endl;
913 #endif
914
915   /// split feature
916   theSplitFeature = createArcFeature(aBaseFeature, aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
917   theCreatedFeatures.insert(theSplitFeature);
918
919   // before split feature
920   if (aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
921     theModifiedAttributes.insert(std::make_pair(aStartPointAttrOfBase,
922                                   theSplitFeature->attribute(SketchPlugin_Arc::START_ID())));
923   }
924   else {
925     theBaseFeatureModified = aBaseFeature; ///< use base feature to store all constraints here
926     /// move end arc point to start of split
927   }
928
929   // after split feature
930   if (!aSecondPointAttrOfSplit->pnt()->isEqual(anEndPointAttrOfBase->pnt())) {
931     FeaturePtr aFeature;
932     if (!theBaseFeatureModified.get()) {
933       aFeature = aBaseFeature; ///< use base feature to store all constraints here
934       fillAttribute(aFeature->attribute(SketchPlugin_Arc::START_ID()), aSecondPointAttrOfSplit);
935       aFeature->execute(); // to update result
936     }
937     else {
938       aFeature = createArcFeature(aBaseFeature, aSecondPointAttrOfSplit, anEndPointAttrOfBase);
939       theCreatedFeatures.insert(aFeature);
940       theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
941                                                   aFeature->attribute(SketchPlugin_Arc::END_ID())));
942     }
943     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
944                      theSplitFeature->attribute(SketchPlugin_Arc::END_ID()),
945                      aFeature->attribute(SketchPlugin_Arc::START_ID()));
946     theCreatedFeatures.insert(aConstraintFeature);
947
948     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
949                                 (aFeature->attribute(SketchPlugin_Arc::START_ID())));
950     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
951                                 (aFeature->attribute(SketchPlugin_Arc::END_ID())));
952
953     if (!theBaseFeatureModified.get())
954       theBaseFeatureModified = aFeature;
955     else
956       theAfterFeature = aFeature;
957   }
958   else {
959     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
960                                   (theSplitFeature->attribute(SketchPlugin_Arc::END_ID())));
961     theModifiedAttributes.insert(std::make_pair(anEndPointAttrOfBase,
962                                    theSplitFeature->attribute(SketchPlugin_Arc::END_ID())));
963   }
964   // base split, that is defined before split feature should be changed at end
965   // (after the after feature creation). Otherwise modified value will be used in after feature
966   // before split feature
967   if (!aStartPointAttrOfBase->pnt()->isEqual(aFirstPointAttrOfSplit->pnt())) {
968     /// move end arc point to start of split
969     fillAttribute(theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID()),
970                                                     aFirstPointAttrOfSplit);
971     theBaseFeatureModified->execute(); // to update result
972     aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
973                      theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID()),
974                      theSplitFeature->attribute(SketchPlugin_Arc::START_ID()));
975     theCreatedFeatures.insert(aConstraintFeature);
976
977     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
978                              (theBaseFeatureModified->attribute(SketchPlugin_Arc::START_ID())));
979     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
980                                (theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID())));
981   }
982   else
983     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
984                                        (theSplitFeature->attribute(SketchPlugin_Arc::START_ID())));
985
986   // additional constraints between split and base features
987   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintEqual::ID(),
988                                                        getFeatureResult(aBaseFeature),
989                                                        getFeatureResult(theSplitFeature));
990   theCreatedFeatures.insert(aConstraintFeature);
991   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintTangent::ID(),
992                                                        getFeatureResult(theSplitFeature),
993                                                        getFeatureResult(aBaseFeature));
994   theCreatedFeatures.insert(aConstraintFeature);
995   if (theAfterFeature.get()) {
996     aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintEqual::ID(),
997                                                     getFeatureResult(aBaseFeature),
998                                                     getFeatureResult(theAfterFeature));
999     theCreatedFeatures.insert(aConstraintFeature);
1000     aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintTangent::ID(),
1001                                                     getFeatureResult(theSplitFeature),
1002                                                     getFeatureResult(theAfterFeature));
1003     theCreatedFeatures.insert(aConstraintFeature);
1004   }
1005 }
1006
1007 void SketchPlugin_ConstraintSplit::splitCircle(FeaturePtr& theSplitFeature,
1008                                                FeaturePtr& theBaseFeatureModified,
1009                                                FeaturePtr& theAfterFeature,
1010                                                std::set<AttributePoint2DPtr>& thePoints,
1011                                                std::set<FeaturePtr>& theCreatedFeatures,
1012                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
1013 {
1014   std::set<FeaturePtr> aCreatedFeatures;
1015   FeaturePtr aConstraintFeature;
1016   theBaseFeatureModified = FeaturePtr(); // it will contain modified base feature
1017
1018   SketchPlugin_Sketch* aSketch = sketch();
1019   if (!aSketch)
1020     return;
1021
1022   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
1023                                            data()->attribute(SketchPlugin_Constraint::VALUE()));
1024   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
1025   std::string aFeatureKind = aBaseFeature->getKind();
1026   if (aFeatureKind != SketchPlugin_Circle::ID())
1027     return;
1028
1029   AttributePoint2DPtr aFirstPointAttrOfSplit = 
1030     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_A()));
1031   AttributePoint2DPtr aSecondPointAttrOfSplit = 
1032     getPointOfRefAttr(data()->attribute(SketchPlugin_Constraint::ENTITY_B()));
1033
1034   /// split feature
1035   theSplitFeature = 
1036     createArcFeature(aBaseFeature, aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
1037   bool aSplitReversed = std::dynamic_pointer_cast<SketchPlugin_Arc>(theSplitFeature)->isReversed();
1038   theCreatedFeatures.insert(theSplitFeature);
1039
1040   /// base feature is a left part of the circle
1041   theBaseFeatureModified = createArcFeature(aBaseFeature, 
1042     aFirstPointAttrOfSplit, aSecondPointAttrOfSplit);
1043   std::dynamic_pointer_cast<SketchPlugin_Arc>(
1044     theBaseFeatureModified)->setReversed(!aSplitReversed);
1045   theBaseFeatureModified->execute();
1046
1047   theModifiedAttributes.insert(
1048     std::make_pair(aBaseFeature->attribute(SketchPlugin_Circle::CENTER_ID()),
1049                   theBaseFeatureModified->attribute(SketchPlugin_Arc::CENTER_ID())));
1050
1051   theCreatedFeatures.insert(theBaseFeatureModified);
1052
1053   thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
1054                              (theBaseFeatureModified->attribute(SketchPlugin_Arc::START_ID())));
1055   thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
1056                              (theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID())));
1057
1058   // additional constraints between split and base features
1059   aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
1060                      theBaseFeatureModified->attribute(SketchPlugin_Arc::END_ID()),
1061                      theSplitFeature->attribute(SketchPlugin_Arc::END_ID()));
1062   theCreatedFeatures.insert(aConstraintFeature);
1063   aConstraintFeature = createConstraint(SketchPlugin_ConstraintCoincidence::ID(),
1064                      theBaseFeatureModified->attribute(SketchPlugin_Arc::START_ID()),
1065                      theSplitFeature->attribute(SketchPlugin_Arc::START_ID()));
1066   theCreatedFeatures.insert(aConstraintFeature);
1067
1068   aConstraintFeature = createConstraintForObjects(SketchPlugin_ConstraintTangent::ID(),
1069                                                        getFeatureResult(theSplitFeature),
1070                                                        getFeatureResult(theBaseFeatureModified));
1071   theCreatedFeatures.insert(aConstraintFeature);
1072 }
1073
1074 void SketchPlugin_ConstraintSplit::arrangePointsOnLine(
1075     const AttributePoint2DPtr& theStartPointAttr,
1076     const AttributePoint2DPtr& theEndPointAttr,
1077     AttributePoint2DPtr& theFirstPointAttr,
1078     AttributePoint2DPtr& theLastPointAttr) const
1079 {
1080   // if first point is closer to last point, swap first and last values
1081   if (theStartPointAttr->pnt()->distance(theFirstPointAttr->pnt()) >
1082       theStartPointAttr->pnt()->distance(theLastPointAttr->pnt())) {
1083     AttributePoint2DPtr aTmpPoint = theFirstPointAttr;
1084     theFirstPointAttr = theLastPointAttr;
1085     theLastPointAttr = aTmpPoint;
1086   }
1087 }
1088
1089 void SketchPlugin_ConstraintSplit::arrangePointsOnArc(
1090     const FeaturePtr& theArc,
1091     const std::shared_ptr<GeomDataAPI_Point2D>& theStartPointAttr,
1092     const std::shared_ptr<GeomDataAPI_Point2D>& theEndPointAttr,
1093     std::shared_ptr<GeomDataAPI_Point2D>& theFirstPointAttr,
1094     std::shared_ptr<GeomDataAPI_Point2D>& theSecondPointAttr) const
1095 {
1096   static const double anAngleTol = 1.e-12;
1097
1098   std::shared_ptr<GeomAPI_Pnt2d> aCenter = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1099       theArc->attribute(SketchPlugin_Arc::CENTER_ID()))->pnt();
1100   bool isReversed = theArc->boolean(SketchPlugin_Arc::INVERSED_ID())->value();
1101
1102   // collect directions to each point
1103   std::shared_ptr<GeomAPI_Dir2d> aStartDir(
1104       new GeomAPI_Dir2d(theStartPointAttr->pnt()->xy()->decreased(aCenter->xy())));
1105   std::shared_ptr<GeomAPI_Dir2d> aFirstPtDir(
1106       new GeomAPI_Dir2d(theFirstPointAttr->pnt()->xy()->decreased(aCenter->xy())));
1107   std::shared_ptr<GeomAPI_Dir2d> aSecondPtDir(
1108       new GeomAPI_Dir2d(theSecondPointAttr->pnt()->xy()->decreased(aCenter->xy())));
1109
1110   // sort points by their angular values
1111   double aFirstPtAngle = aStartDir->angle(aFirstPtDir);
1112   double aSecondPtAngle = aStartDir->angle(aSecondPtDir);
1113   double aPeriod = isReversed ? -2.0 * PI : 2.0 * PI;
1114   if (fabs(aFirstPtAngle) > anAngleTol && isReversed == (aFirstPtAngle > 0.))
1115     aFirstPtAngle += aPeriod;
1116   if (fabs(aSecondPtAngle) > anAngleTol && isReversed == (aSecondPtAngle > 0.))
1117     aSecondPtAngle += aPeriod;
1118
1119   if (fabs(aFirstPtAngle) > fabs(aSecondPtAngle)) {
1120     AttributePoint2DPtr aTmpPoint = theFirstPointAttr;
1121     theFirstPointAttr = theSecondPointAttr;
1122     theSecondPointAttr = aTmpPoint;
1123   }
1124 }
1125
1126 void SketchPlugin_ConstraintSplit::fillAttribute(const AttributePtr& theModifiedAttribute,
1127                                                  const AttributePtr& theSourceAttribute)
1128 {
1129   AttributePoint2DPtr aModifiedAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1130                                             theModifiedAttribute);
1131   AttributePoint2DPtr aSourceAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1132                                             theSourceAttribute);
1133
1134   if (aModifiedAttribute.get() && aSourceAttribute.get())
1135     aModifiedAttribute->setValue(aSourceAttribute->pnt());
1136 }
1137
1138 FeaturePtr SketchPlugin_ConstraintSplit::createLineFeature(const FeaturePtr& theBaseFeature,
1139                                                            const AttributePtr& theFirstPointAttr,
1140                                                            const AttributePtr& theSecondPointAttr)
1141 {
1142   FeaturePtr aFeature;
1143   SketchPlugin_Sketch* aSketch = sketch();
1144   if (!aSketch || !theBaseFeature.get())
1145     return aFeature;
1146
1147   aFeature = aSketch->addFeature(SketchPlugin_Line::ID());
1148
1149   fillAttribute(aFeature->attribute(SketchPlugin_Line::START_ID()), theFirstPointAttr);
1150   fillAttribute(aFeature->attribute(SketchPlugin_Line::END_ID()), theSecondPointAttr);
1151   aFeature->execute(); // to obtain result
1152
1153   return aFeature;
1154 }
1155
1156 FeaturePtr SketchPlugin_ConstraintSplit::createArcFeature(const FeaturePtr& theBaseFeature,
1157                                                           const AttributePtr& theFirstPointAttr,
1158                                                           const AttributePtr& theSecondPointAttr)
1159 {
1160   FeaturePtr aFeature;
1161   SketchPlugin_Sketch* aSketch = sketch();
1162   if (!aSketch || !theBaseFeature.get())
1163     return aFeature;
1164
1165   std::string aCenterAttributeId;
1166   if (theBaseFeature->getKind() == SketchPlugin_Arc::ID())
1167     aCenterAttributeId = SketchPlugin_Arc::CENTER_ID();
1168   else if (theBaseFeature->getKind() == SketchPlugin_Circle::ID())
1169     aCenterAttributeId = SketchPlugin_Circle::CENTER_ID();
1170
1171   if (aCenterAttributeId.empty())
1172     return aFeature;
1173
1174   aFeature = aSketch->addFeature(SketchPlugin_Arc::ID());
1175   // update fillet arc: make the arc correct for sure, so, it is not needed to process 
1176   // the "attribute updated"
1177   // by arc; moreover, it may cause cyclicity in hte mechanism of updater
1178   aFeature->data()->blockSendAttributeUpdated(true);
1179
1180   aFeature->string(SketchPlugin_Arc::ARC_TYPE())->setValue(
1181                 SketchPlugin_Arc::ARC_TYPE_CENTER_START_END());
1182
1183   fillAttribute(aFeature->attribute(SketchPlugin_Arc::CENTER_ID()),
1184                 theBaseFeature->attribute(aCenterAttributeId));
1185   fillAttribute(aFeature->attribute(SketchPlugin_Arc::START_ID()), theFirstPointAttr);
1186   fillAttribute(aFeature->attribute(SketchPlugin_Arc::END_ID()), theSecondPointAttr);
1187
1188   /// fill referersed state of created arc as it is on the base arc
1189   if (theBaseFeature->getKind() == SketchPlugin_Arc::ID()) {
1190     bool aReversed = theBaseFeature->boolean(SketchPlugin_Arc::INVERSED_ID())->value();
1191     aFeature->boolean(SketchPlugin_Arc::INVERSED_ID())->setValue(aReversed);
1192   }
1193   aFeature->data()->blockSendAttributeUpdated(false);
1194   aFeature->execute(); // to obtain result
1195
1196   return aFeature;
1197 }
1198
1199 FeaturePtr SketchPlugin_ConstraintSplit::createConstraint(const std::string& theConstraintId,
1200                                                     const AttributePtr& theFirstAttribute,
1201                                                     const AttributePtr& theSecondAttribute)
1202 {
1203   FeaturePtr aConstraint = sketch()->addFeature(theConstraintId);
1204   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1205                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
1206   aRefAttr->setAttr(theFirstAttribute);
1207
1208   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1209                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
1210   aRefAttr->setAttr(theSecondAttribute);
1211
1212   return aConstraint;
1213 }
1214
1215 FeaturePtr SketchPlugin_ConstraintSplit::createConstraintForObjects(
1216                                                     const std::string& theConstraintId,
1217                                                     const ObjectPtr& theFirstObject,
1218                                                     const ObjectPtr& theSecondObject)
1219 {
1220   FeaturePtr aConstraint = sketch()->addFeature(theConstraintId);
1221   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1222                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_A()));
1223   aRefAttr->setObject(theFirstObject);
1224
1225   aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
1226                                  aConstraint->attribute(SketchPlugin_Constraint::ENTITY_B()));
1227   aRefAttr->setObject(theSecondObject);
1228
1229   return aConstraint;
1230 }
1231
1232 void SketchPlugin_ConstraintSplit::updateFeaturesAfterSplit(
1233                                                    const std::set<FeaturePtr>& theFeaturesToUpdate)
1234 {
1235   std::set<FeaturePtr>::const_iterator anIt = theFeaturesToUpdate.begin(),
1236                                        aLast = theFeaturesToUpdate.end();
1237   for (; anIt != aLast; anIt++) {
1238     FeaturePtr aRefFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
1239     std::string aRefFeatureKind = aRefFeature->getKind();
1240     if (aRefFeatureKind == SketchPlugin_ConstraintLength::ID()) {
1241       std::shared_ptr<SketchPlugin_ConstraintLength> aLenghtFeature =
1242                               std::dynamic_pointer_cast<SketchPlugin_ConstraintLength>(*anIt);
1243       if (aLenghtFeature.get()) {
1244         std::shared_ptr<ModelAPI_AttributeDouble> aValueAttr = std::dynamic_pointer_cast<
1245             ModelAPI_AttributeDouble>(aLenghtFeature->attribute(SketchPlugin_Constraint::VALUE()));
1246         double aValue;
1247         if (aLenghtFeature->computeLenghtValue(aValue) && aValueAttr.get())
1248           aValueAttr->setValue(aValue);
1249       }
1250     }
1251   }
1252 }
1253
1254 std::shared_ptr<ModelAPI_Result> SketchPlugin_ConstraintSplit::getFeatureResult(
1255                                     const std::shared_ptr<ModelAPI_Feature>& theFeature)
1256 {
1257   std::shared_ptr<ModelAPI_Result> aResult;
1258
1259   std::string aFeatureKind = theFeature->getKind();
1260   if (aFeatureKind == SketchPlugin_Line::ID())
1261     aResult = theFeature->firstResult();
1262   else if (aFeatureKind == SketchPlugin_Arc::ID())
1263     aResult = theFeature->lastResult();
1264   else if (aFeatureKind == SketchPlugin_Circle::ID())
1265     aResult = theFeature->lastResult();
1266
1267   return aResult;
1268 }
1269
1270 std::set<std::shared_ptr<ModelAPI_Attribute> > SketchPlugin_ConstraintSplit::getEdgeAttributes(
1271                                            const std::shared_ptr<ModelAPI_Feature>& theFeature)
1272 {
1273   std::set<std::shared_ptr<ModelAPI_Attribute> > anAttributes;
1274
1275   std::string aFeatureKind = theFeature->getKind();
1276   if (aFeatureKind == SketchPlugin_Line::ID()) {
1277     anAttributes.insert(theFeature->attribute(SketchPlugin_Line::START_ID()));
1278     anAttributes.insert(theFeature->attribute(SketchPlugin_Line::END_ID()));
1279   }
1280   else if (aFeatureKind == SketchPlugin_Arc::ID()) {
1281     anAttributes.insert(theFeature->attribute(SketchPlugin_Arc::START_ID()));
1282     anAttributes.insert(theFeature->attribute(SketchPlugin_Arc::END_ID()));
1283   }
1284   else if (aFeatureKind == SketchPlugin_Circle::ID()) {
1285   }
1286
1287   return anAttributes;
1288 }
1289
1290
1291 std::string SketchPlugin_ConstraintSplit::getFeatureInfo(
1292                                                const std::shared_ptr<ModelAPI_Feature>& theFeature,
1293                                                const bool isUseAttributesInfo)
1294 {
1295   std::string anInfo;
1296   if (!theFeature.get()) {
1297     return "none";
1298   }
1299
1300   if (theFeature->data()->isValid())
1301     anInfo.append(theFeature->data()->name().c_str());
1302
1303   if (isUseAttributesInfo) {
1304     std::string aPointsInfo = ModelGeomAlgo_Point2D::getPontAttributesInfo(theFeature,
1305                                                              getEdgeAttributes(theFeature));
1306     /// processing of feature with point 2d attributes, like line, arc, circle
1307     if (!aPointsInfo.empty()) { 
1308       anInfo += ": ";
1309       anInfo += "\n";
1310       anInfo += aPointsInfo;
1311     }
1312     else { /// process constraint coincidence, find points in ref attr attributes
1313       std::list<AttributePtr> anAttrs = theFeature->data()->attributes(
1314                                                        ModelAPI_AttributeRefAttr::typeId());
1315       std::list<AttributePtr>::const_iterator anIt = anAttrs.begin(), aLast = anAttrs.end();
1316       std::string anAttributesInfo;
1317       for(; anIt != aLast; anIt++) {
1318         if (!anAttributesInfo.empty()) {
1319           anAttributesInfo.append(", ");
1320           anAttributesInfo += "\n";
1321         }
1322         AttributePtr anAttr = *anIt;
1323         std::string aValue = "not defined";
1324         std::string aType = anAttr->attributeType();
1325         if (aType == ModelAPI_AttributeRefAttr::typeId()) {
1326           std::shared_ptr<ModelAPI_AttributeRefAttr> aRefAttr =
1327                              std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttr);
1328           if (aRefAttr.get()) {
1329             if (aRefAttr->isObject()) {
1330               FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
1331               aValue = "<object:>" + getFeatureInfo(aFeature, false);
1332             }
1333             else {
1334               AttributePtr anAttribute = aRefAttr->attr();
1335               if (anAttribute.get()) {
1336                 FeaturePtr aFeature = ModelAPI_Feature::feature(anAttribute->owner());
1337                 aValue = "<attr:>" + ModelGeomAlgo_Point2D::getPointAttributeInfo(anAttribute) +
1338                          " [" + getFeatureInfo(aFeature, false) + "]";
1339               }
1340             }
1341           }
1342         }
1343         anAttributesInfo.append("    " + anAttr->id() + ": " + aValue);
1344       }
1345       if (!anAttributesInfo.empty())
1346         anInfo = anInfo + "\n" + anAttributesInfo;
1347     }
1348   }
1349   return anInfo;
1350 }
1351