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