Salome HOME
updated copyright message
[modules/shaper.git] / src / SketchPlugin / SketchPlugin_Trim.cpp
1 // Copyright (C) 2014-2023  CEA, EDF
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "SketchPlugin_Trim.h"
21
22 #include <GeomAPI_Dir2d.h>
23 #include <GeomAPI_Edge.h>
24 #include <GeomAPI_Pnt2d.h>
25 #include <GeomAPI_XY.h>
26 #include <GeomDataAPI_Point2D.h>
27 #include <GeomAlgoAPI_ShapeTools.h>
28
29 #include <ModelAPI_AttributeReference.h>
30 #include <ModelAPI_AttributeString.h>
31 #include <ModelAPI_AttributeRefAttr.h>
32 #include <ModelAPI_Tools.h>
33 #include <ModelAPI_AttributeBoolean.h>
34
35 #include <ModelAPI_Validator.h>
36 #include <ModelAPI_Session.h>
37 #include <ModelAPI_AttributeDouble.h>
38
39 #include <ModelGeomAlgo_Shape.h>
40
41 #include <SketchPlugin_Arc.h>
42 #include <SketchPlugin_ConstraintMiddle.h>
43 #include <SketchPlugin_Circle.h>
44 #include <SketchPlugin_ConstraintCoincidence.h>
45 #include <SketchPlugin_ConstraintEqual.h>
46 #include <SketchPlugin_ConstraintTangent.h>
47 #include <SketchPlugin_ConstraintLength.h>
48 #include <SketchPlugin_ConstraintMirror.h>
49 #include <SketchPlugin_ConstraintCollinear.h>
50 #include <SketchPlugin_Ellipse.h>
51 #include <SketchPlugin_EllipticArc.h>
52 #include <SketchPlugin_Line.h>
53 #include <SketchPlugin_MultiRotation.h>
54 #include <SketchPlugin_MultiTranslation.h>
55 #include <SketchPlugin_Point.h>
56
57 #include <ModelAPI_EventReentrantMessage.h>
58
59 #include <ModelAPI_Events.h>
60 #include <SketchPlugin_Line.h>
61 #include <SketchPlugin_Arc.h>
62 #include <SketchPlugin_Circle.h>
63
64 #include <ModelGeomAlgo_Point2D.h>
65 #include <Events_Loop.h>
66
67 #include <cmath>
68
69 #ifdef DEBUG_TRIM
70 #include <iostream>
71 #endif
72
73 #ifdef DEBUG_TRIM_METHODS
74 #include <iostream>
75 #endif
76
77 static const double PI = 3.141592653589793238463;
78
79 SketchPlugin_Trim::SketchPlugin_Trim()
80 {
81 }
82
83 void SketchPlugin_Trim::initAttributes()
84 {
85   data()->addAttribute(SELECTED_OBJECT(), ModelAPI_AttributeReference::typeId());
86   data()->addAttribute(SELECTED_POINT(), GeomDataAPI_Point2D::typeId());
87
88   data()->addAttribute(PREVIEW_POINT(), GeomDataAPI_Point2D::typeId());
89   data()->addAttribute(PREVIEW_OBJECT(), ModelAPI_AttributeReference::typeId());
90
91   data()->attribute(PREVIEW_POINT())->setIsArgument(false);
92   data()->attribute(SELECTED_POINT())->setIsArgument(false);
93   data()->attribute(PREVIEW_OBJECT())->setIsArgument(false);
94
95   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PREVIEW_POINT());
96   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), PREVIEW_OBJECT());
97 }
98
99 void SketchPlugin_Trim::findShapePoints(const std::string& theObjectAttributeId,
100                                         const std::string& thePointAttributeId,
101                                         std::shared_ptr<GeomAPI_Pnt>& aStartPoint,
102                                         std::shared_ptr<GeomAPI_Pnt>& aLastPoint)
103 {
104   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
105                                             data()->attribute(theObjectAttributeId));
106   ObjectPtr aBaseObject = aBaseObjectAttr->value();
107
108   AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
109                                               data()->attribute(thePointAttributeId));
110   std::shared_ptr<GeomAPI_Pnt2d> anAttributePnt2d = aPoint->pnt();
111   std::shared_ptr<GeomAPI_Pnt> anAttributePnt = sketch()->to3D(anAttributePnt2d->x(),
112                                                                anAttributePnt2d->y());
113
114   if (myCashedShapes.find(aBaseObject) == myCashedShapes.end()) {
115     SketchPlugin_SegmentationTools::fillObjectShapes(
116         this, aBaseObject, myCashedShapes, myObjectToPoints);
117   }
118
119   const std::set<GeomShapePtr>& aShapes = myCashedShapes[aBaseObject];
120   if (!aShapes.empty()) {
121     std::set<GeomShapePtr>::const_iterator anIt = aShapes.begin(), aLast = aShapes.end();
122     for (; anIt != aLast; anIt++) {
123       GeomShapePtr aBaseShape = *anIt;
124       std::shared_ptr<GeomAPI_Pnt> aProjectedPoint;
125       if (ModelGeomAlgo_Point2D::isPointOnEdge(aBaseShape, anAttributePnt, aProjectedPoint)) {
126
127         if (aBaseShape->shapeType() == GeomAPI_Shape::EDGE) {
128           std::shared_ptr<GeomAPI_Edge> anEdge(new GeomAPI_Edge(aBaseShape));
129           //GeomAPI_Shape::Orientation anOrientation = anEdge->orientation();
130           //if (anOrientation == GeomAPI_Shape::REVERSED) {
131             aStartPoint = anEdge->lastPoint();
132             aLastPoint = anEdge->firstPoint();
133           //}
134           //else {
135             //aStartPoint = anEdge->firstPoint();
136             //aLastPoint = anEdge->lastPoint();
137           //}
138         }
139       }
140     }
141   }
142 #ifdef DEBUG_TRIM
143   std::cout << "<findShapePoints> => "
144     << std::endl << "Attribute point: "
145     << anAttributePnt->x() << ", " << anAttributePnt->y() << ", " << anAttributePnt->z() << "]"
146     << std::endl << "Start Point: ["
147     << aStartPoint->x() << ", " << aStartPoint->y() << ", " << aStartPoint->z() << "]"
148     << std::endl << "Last Point: ["
149     << aLastPoint->x() << ", " << aLastPoint->y() << ", " << aLastPoint->z() << "]"
150     << std::endl;
151 #endif
152 }
153
154 std::shared_ptr<GeomAPI_Pnt2d> SketchPlugin_Trim::convertPoint(
155                                                    const std::shared_ptr<GeomAPI_Pnt>& thePoint)
156 {
157   std::shared_ptr<GeomAPI_Pnt2d> aPoint;
158   if (!thePoint.get())
159     return aPoint;
160
161   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
162                                         data()->attribute(SketchPlugin_Trim::SELECTED_OBJECT()));
163   ObjectPtr aBaseObject = aBaseObjectAttr->value();
164   if (myObjectToPoints.find(aBaseObject) == myObjectToPoints.end()) {
165     SketchPlugin_SegmentationTools::fillObjectShapes(
166         this, aBaseObject, myCashedShapes, myObjectToPoints);
167   }
168
169   bool aFound = false;
170   const GeomAlgoAPI_ShapeTools::PointToRefsMap& aRefsMap = myObjectToPoints.at(aBaseObject);
171   for (GeomAlgoAPI_ShapeTools::PointToRefsMap::const_iterator aPointIt = aRefsMap.begin();
172        aPointIt != aRefsMap.end() && !aFound; aPointIt++) {
173     if (aPointIt->first->isEqual(thePoint)) {
174       const std::pair<std::list<AttributePoint2DPtr >,
175                std::list<ObjectPtr > >& anInfo = aPointIt->second;
176       const std::list<AttributePoint2DPtr >& anAttributes = anInfo.first;
177       if (!anAttributes.empty()) {
178         aPoint = anAttributes.front()->pnt();
179         aFound = true;
180       }
181       else {
182         aPoint = sketch()->to2D(thePoint);
183         aFound = true;
184       }
185     }
186   }
187   if (!aFound) {
188     // returns an end of the shape to define direction of split if feature's attribute
189     // participates
190     aPoint = sketch()->to2D(thePoint);
191   }
192   return aPoint;
193 }
194
195 void SketchPlugin_Trim::execute()
196 {
197 #ifdef DEBUG_TRIM_METHODS
198   std::cout << "SketchPlugin_Trim::execute: " << data()->name() << std::endl;
199 #endif
200
201   SketchPlugin_Sketch* aSketch = sketch();
202   if (!aSketch) {
203     setError("Error: Sketch object is empty.");
204     return;
205   }
206
207   // Check the base objects are initialized.
208   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
209                                         data()->attribute(SketchPlugin_Trim::SELECTED_OBJECT()));
210   if(!aBaseObjectAttr->isInitialized()) {
211     setError("Error: Base object is not initialized.");
212     return;
213   }
214   ObjectPtr aBaseObject = aBaseObjectAttr->value();
215   if (!aBaseObject.get()) {
216     setError("Error: Base object is not initialized.");
217     return;
218   }
219   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
220
221   /// Remove reference of this feature to feature used in preview, it is not necessary anymore
222   /// as trim will be removed after execute
223   AttributeReferencePtr aPreviewObjectAttr =
224                      std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
225                      data()->attribute(SketchPlugin_Trim::PREVIEW_OBJECT()));
226
227   ObjectPtr aPreviewObject = aPreviewObjectAttr->value();
228   AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
229                                            data()->attribute(PREVIEW_POINT()));
230   std::shared_ptr<GeomAPI_Pnt2d> aPreviewPnt2d = aPoint->pnt();
231   // nullify pointer of preview attribute
232   aPreviewObjectAttr->setValue(ResultPtr());
233
234   bool anIsEqualPreviewAndSelected = aPreviewObject == aBaseObject;
235
236   /// points of trim
237   std::shared_ptr<GeomAPI_Pnt> aStartShapePoint, aLastShapePoint;
238 #ifdef DEBUG_TRIM
239   std::cout << " Base Feature: " << aBaseFeature->data()->name() << std::endl;
240 #endif
241   findShapePoints(SELECTED_OBJECT(), SELECTED_POINT(), aStartShapePoint, aLastShapePoint);
242   if (!aStartShapePoint || !aLastShapePoint) {
243     setError("Error: Selected point is not placed on any edge");
244     return;
245   }
246
247   std::shared_ptr<GeomAPI_Pnt2d> aStartShapePoint2d = convertPoint(aStartShapePoint);
248   std::shared_ptr<GeomAPI_Pnt2d> aLastShapePoint2d = convertPoint(aLastShapePoint);
249   /// find features that should be deleted (e.g. Middle Point) or updated (e.g. Length)
250   std::set<FeaturePtr> aFeaturesToDelete, aFeaturesToUpdate;
251   getConstraints(aFeaturesToDelete, aFeaturesToUpdate);
252   // find references(attributes and features) to the base feature
253   std::map<AttributePtr, std::list<AttributePtr> > aBaseRefAttributes;
254   std::list<AttributePtr> aRefsToFeature;
255   SketchPlugin_SegmentationTools::getRefAttributes(
256       aBaseFeature, aBaseRefAttributes, aRefsToFeature);
257 #ifdef DEBUG_TRIM
258   std::cout << "---- getRefAttributes ----" << std::endl;
259   std::map<AttributePtr, std::list<AttributePtr> >::const_iterator
260     aRefIt = aBaseRefAttributes.begin(), aRefLast = aBaseRefAttributes.end();
261   std::cout << std::endl << "References to attributes of base feature [" <<
262     aBaseRefAttributes.size() << "]" << std::endl;
263   for (; aRefIt != aRefLast; aRefIt++) {
264     AttributePtr aBaseAttr = aRefIt->first;
265     std::list<AttributePtr> aRefAttributes = aRefIt->second;
266     std::string aRefsInfo;
267     std::list<AttributePtr>::const_iterator aRefAttrIt = aRefAttributes.begin(),
268                                             aRefAttrLast = aRefAttributes.end();
269     for (; aRefAttrIt != aRefAttrLast; aRefAttrIt++) {
270       if (!aRefsInfo.empty())
271         aRefsInfo.append(",");
272       AttributePtr aRAttr = *aRefAttrIt;
273       aRefsInfo.append(aRAttr->id());
274       FeaturePtr aRFeature = ModelAPI_Feature::feature(aRAttr->owner());
275       aRefsInfo.append("(" + aRFeature->name() + ") ");
276     }
277     std::shared_ptr<GeomDataAPI_Point2D> aPointAttr =
278       std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aBaseAttr);
279     std::cout << aPointAttr->id().c_str() <<
280       ": " << "[" << aRefAttributes.size() << "] " << aRefsInfo << std::endl;
281   }
282   std::cout << std::endl;
283   std::cout << std::endl << "References to base feature [" <<
284     aRefsToFeature.size() << "]" << std::endl;
285   std::list<AttributePtr>::const_iterator aRefAttrIt = aRefsToFeature.begin(),
286                                           aRefAttrLast = aRefsToFeature.end();
287   std::string aRefsInfo;
288   for (; aRefAttrIt != aRefAttrLast; aRefAttrIt++) {
289     if (!aRefsInfo.empty())
290       aRefsInfo.append(",");
291     AttributePtr aRAttr = *aRefAttrIt;
292     aRefsInfo.append(aRAttr->id());
293     FeaturePtr aRFeature = ModelAPI_Feature::feature(aRAttr->owner());
294     aRefsInfo.append("(" + aRFeature->name() + ") ");
295   }
296   std::cout << "[" << aRefsToFeature.size() << "] " << aRefsInfo << std::endl;
297   std::cout << "---- getRefAttributes:end ----" << std::endl;
298 #endif
299
300   keepCurrentFeature();
301
302   std::set<AttributePoint2DPtr> aFurtherCoincidences;
303   std::set<std::pair<AttributePtr, AttributePtr>> aModifiedAttributes;
304   const std::string& aKind = aBaseFeature->getKind();
305   FeaturePtr aReplacingFeature, aNewFeature;
306   if (aKind == SketchPlugin_Circle::ID() ||
307       aKind == SketchPlugin_Ellipse::ID()) {
308     aReplacingFeature = trimClosed(aStartShapePoint2d, aLastShapePoint2d,
309                aFurtherCoincidences, aModifiedAttributes);
310
311     aFeaturesToDelete.insert(aBaseFeature);
312     // as circle is removed, erase it from dependencies(arguments) of this feature
313     // otherwise Trim feature will be removed with the circle before
314     // this operation is finished
315     aBaseObjectAttr->setObject(ResultPtr());
316   }
317   else if (aKind == SketchPlugin_Line::ID()) {
318     aNewFeature = trimLine(aStartShapePoint2d, aLastShapePoint2d, aBaseRefAttributes,
319                            aFurtherCoincidences, aModifiedAttributes);
320   }
321   else if (aKind == SketchPlugin_Arc::ID()) {
322     aNewFeature = trimArc(aStartShapePoint2d, aLastShapePoint2d, aBaseRefAttributes,
323                           aFurtherCoincidences, aModifiedAttributes);
324   }
325   else if (aKind == SketchPlugin_EllipticArc::ID()) {
326     aNewFeature = trimEllipticArc(aStartShapePoint2d, aLastShapePoint2d, aBaseRefAttributes,
327                           aFurtherCoincidences, aModifiedAttributes);
328   }
329
330   restoreCurrentFeature();
331
332   // constraints to end points of trim feature
333   if (myObjectToPoints.find(aBaseObject) == myObjectToPoints.end()) {
334     SketchPlugin_SegmentationTools::fillObjectShapes(
335         this, aBaseObject, myCashedShapes, myObjectToPoints);
336   }
337
338   // create coincidence to objects, intersected the base object
339   const GeomAlgoAPI_ShapeTools::PointToRefsMap& aRefsMap = myObjectToPoints.at(aBaseObject);
340   for (std::set<AttributePoint2DPtr>::const_iterator anIt = aFurtherCoincidences.begin(),
341                                                      aLast = aFurtherCoincidences.end();
342        anIt != aLast; anIt++) {
343     AttributePoint2DPtr aPointAttribute = (*anIt);
344     std::shared_ptr<GeomAPI_Pnt2d> aPoint2d = aPointAttribute->pnt();
345
346 #ifdef DEBUG_TRIM
347     std::cout << "<compare Points> => " << std::endl
348             << "aPoint2d: [" << aPoint2d->x() << ", " << aPoint2d->y() << "]" << std::endl;
349     if (aStartShapePoint2d.get())
350       std::cout << "Start Point: [" << aStartShapePoint2d->x() << ", " << aStartShapePoint2d->y()
351                 << "]" << std::endl;
352     if (aLastShapePoint2d.get())
353       std::cout << "Last Point: [" << aLastShapePoint2d->x() << ", " << aLastShapePoint2d->y()
354                 << "]" << std::endl;
355 #endif
356
357     std::shared_ptr<GeomAPI_Pnt> aExtrPoint;
358     if (aStartShapePoint2d.get() && aPoint2d->isEqual(aStartShapePoint2d))
359       aExtrPoint = aStartShapePoint;
360     else if (aLastShapePoint2d.get() && aPoint2d->isEqual(aLastShapePoint2d))
361       aExtrPoint = aLastShapePoint;
362
363     if (!aExtrPoint.get())
364       continue;
365
366     std::pair<std::list<AttributePoint2DPtr >, std::list<ObjectPtr > > anInfo;
367     for (GeomAlgoAPI_ShapeTools::PointToRefsMap::const_iterator aRefIt = aRefsMap.begin();
368          aRefIt != aRefsMap.end(); aRefIt++)
369     {
370       if (aRefIt->first->isEqual(aExtrPoint)) {
371         anInfo = aRefIt->second;
372         // prefer a segment instead of a point, because further coincidence with a segment
373         // decreases only 1 DoF (instead of 2 for point) and prevents an overconstraint situation.
374         bool isEdge = false;
375         for (std::list<ObjectPtr>::const_iterator anInfoIt = anInfo.second.begin();
376              anInfoIt != anInfo.second.end() && !isEdge; ++anInfoIt) {
377           ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(*anInfoIt);
378           if (aResult) {
379             GeomShapePtr aShape = aResult->shape();
380             isEdge = aShape && aShape->isEdge();
381           }
382         }
383         if (isEdge)
384           break;
385       }
386     }
387     const std::list<ObjectPtr>& anObjects = anInfo.second;
388     for (std::list<ObjectPtr>::const_iterator anObjectIt = anObjects.begin();
389       anObjectIt != anObjects.end(); anObjectIt++) {
390       SketchPlugin_Tools::createConstraintAttrObject(sketch(),
391             SketchPlugin_ConstraintCoincidence::ID(),
392             aPointAttribute, *anObjectIt);
393     }
394   }
395
396   // move constraints from base feature to replacing feature: ignore coincidences to feature
397   // if attributes of coincidence participated in split
398   ResultPtr aReplacingResult;
399   if (aReplacingFeature.get()) {
400     aReplacingFeature->execute(); // need it to obtain result
401     aReplacingResult = aReplacingFeature->lastResult();
402   }
403   for(std::list<AttributePtr>::const_iterator anIt = aRefsToFeature.begin(),
404                                           aLast = aRefsToFeature.end();
405       anIt != aLast; anIt++) {
406     AttributePtr anAttribute = *anIt;
407
408     if (setCoincidenceToAttribute(anAttribute, aFurtherCoincidences, aFeaturesToDelete))
409       continue;
410
411     // move tangency constraint to the nearest feature if possible
412     if (aNewFeature.get() && moveTangency(anAttribute, aNewFeature))
413       continue;
414
415     if (aReplacingResult.get()) {
416       AttributeRefAttrPtr aRefAttr =
417           std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(anAttribute);
418       if (aRefAttr.get())
419         aRefAttr->setObject(aReplacingResult);
420       else {
421         AttributeReferencePtr aReferenceAttr =
422                              std::dynamic_pointer_cast<ModelAPI_AttributeReference>(anAttribute);
423         if (aReferenceAttr.get())
424           aReferenceAttr->setObject(aReplacingResult);
425       }
426     }
427   }
428
429   SketchPlugin_SegmentationTools::updateRefAttConstraints(aBaseRefAttributes, aModifiedAttributes);
430
431   // Wait all constraints being created, then send update events
432   static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
433   bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
434   if (isUpdateFlushed)
435     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
436
437   // delete constraints
438 #ifdef DEBUG_TRIM
439   if (aFeaturesToDelete.size() > 0) {
440     std::cout << "after SPlit: removeFeaturesAndReferences: " << std::endl;
441     std::string aValue;
442     for (std::set<FeaturePtr>::const_iterator anIt = aFeaturesToDelete.begin();
443          anIt != aFeaturesToDelete.end(); anIt++) {
444       FeaturePtr aFeature = *anIt;
445       std::cout << aFeature->data()->name() << std::endl;
446     }
447   }
448 #endif
449   ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToDelete);
450   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
451
452   SketchPlugin_SegmentationTools::updateFeaturesAfterOperation(aFeaturesToUpdate);
453
454   // Send events to update the sub-features by the solver.
455   if(isUpdateFlushed) {
456     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
457   }
458
459   if (anIsEqualPreviewAndSelected) {
460     // equal preview and selected objects
461     // nothing to do if the preview and selected objects are different
462     if (aReplacingResult.get()) { // base object was removed
463       aPreviewObject = aReplacingResult;
464       //aMessage->setSelectedObject(aReplacingResult);
465 #ifdef DEBUG_TRIM_METHODS
466       if (!aSelectedShape.get())
467         std::cout << "Set empty selected object" << std::endl;
468       else
469         std::cout << "Set shape with ShapeType: " << aSelectedShape->shapeTypeStr() << std::endl;
470 #endif
471     }
472     else {
473       aPreviewObject = ObjectPtr();
474
475       aBaseFeature->execute(); // should recompute shapes of result to do not check obsolete one
476       aBaseObject = aBaseFeature->lastResult();
477       std::shared_ptr<GeomAPI_Pnt> aPreviewPnt = sketch()->to3D(aPreviewPnt2d->x(),
478                                                                 aPreviewPnt2d->y());
479       ResultPtr aBaseResult = std::dynamic_pointer_cast<ModelAPI_Result>(aBaseObject);
480       if (aBaseResult) {
481         GeomShapePtr aShape = aBaseResult->shape();
482         std::shared_ptr<GeomAPI_Pnt> aProjectedPoint;
483         if (ModelGeomAlgo_Point2D::isPointOnEdge(aShape, aPreviewPnt, aProjectedPoint))
484           aPreviewObject = aBaseResult;
485       }
486       if (!aPreviewObject.get() && aNewFeature.get()) {
487         ResultPtr aNewFeatureResult = aNewFeature->lastResult();
488         if (aNewFeatureResult.get()) {
489           GeomShapePtr aShape = aNewFeatureResult->shape();
490           std::shared_ptr<GeomAPI_Pnt> aProjectedPoint;
491           if (ModelGeomAlgo_Point2D::isPointOnEdge(aShape, aPreviewPnt, aProjectedPoint))
492             aPreviewObject = aNewFeatureResult;
493         }
494       }
495     }
496   }
497   if (aPreviewObject.get()) {
498     std::shared_ptr<ModelAPI_EventReentrantMessage> aMessage = std::shared_ptr
499       <ModelAPI_EventReentrantMessage>(new ModelAPI_EventReentrantMessage(
500                                            ModelAPI_EventReentrantMessage::eventId(), this));
501     aMessage->setSelectedObject(aPreviewObject);
502     Events_Loop::loop()->send(aMessage);
503   }
504 #ifdef DEBUG_TRIM
505   std::cout << "SketchPlugin_Trim::done" << std::endl;
506 #endif
507 }
508
509 // LCOV_EXCL_START
510 std::string SketchPlugin_Trim::processEvent(const std::shared_ptr<Events_Message>& theMessage)
511 {
512 #ifdef DEBUG_TRIM_METHODS
513   std::cout << "SketchPlugin_Trim::processEvent:" << data()->name() << std::endl;
514 #endif
515   std::string aFilledAttributeName;
516
517   std::shared_ptr<ModelAPI_EventReentrantMessage> aMessage =
518         std::dynamic_pointer_cast<ModelAPI_EventReentrantMessage>(theMessage);
519   if (aMessage.get()) {
520     ObjectPtr anObject = aMessage->selectedObject();
521     std::shared_ptr<GeomAPI_Pnt2d> aPoint = aMessage->clickedPoint();
522
523     if (anObject.get() && aPoint.get()) {
524       if (myCashedShapes.find(anObject) == myCashedShapes.end()) {
525         SketchPlugin_SegmentationTools::fillObjectShapes(
526             this, anObject, myCashedShapes, myObjectToPoints);
527       }
528       const std::set<GeomShapePtr>& aShapes = myCashedShapes[anObject];
529       if (aShapes.size() > 1) {
530         std::shared_ptr<ModelAPI_AttributeReference> aRefSelectedAttr =
531                               std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
532                               data()->attribute(SketchPlugin_Trim::SELECTED_OBJECT()));
533         std::shared_ptr<ModelAPI_AttributeReference> aRefPreviewAttr =
534                               std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
535                               data()->attribute(SketchPlugin_Trim::PREVIEW_OBJECT()));
536         aRefSelectedAttr->setValue(anObject);
537         aRefPreviewAttr->setValue(anObject);
538
539         std::shared_ptr<GeomDataAPI_Point2D> aPointSelectedAttr =
540                               std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
541                               data()->attribute(SketchPlugin_Trim::SELECTED_POINT()));
542         std::shared_ptr<GeomDataAPI_Point2D> aPointPreviewAttr =
543                               std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
544                               data()->attribute(SketchPlugin_Trim::PREVIEW_POINT()));
545         aPointSelectedAttr->setValue(aPoint);
546         aPointPreviewAttr->setValue(aPoint);
547
548         Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
549
550         GeomShapePtr aSelectedShape = SketchPlugin_SegmentationTools::getSubShape(this,
551             SELECTED_OBJECT(), SELECTED_POINT(), myCashedShapes, myObjectToPoints);
552   #ifdef DEBUG_TRIM_METHODS
553         if (!aSelectedShape.get())
554           std::cout << "Set empty selected object" << std::endl;
555         else
556           std::cout << "Set shape with ShapeType: " << aSelectedShape->shapeTypeStr() << std::endl;
557   #endif
558         aFilledAttributeName = SketchPlugin_Trim::SELECTED_OBJECT();
559       }
560     }
561   }
562   return aFilledAttributeName;
563 }
564 // LCOV_EXCL_STOP
565
566 bool SketchPlugin_Trim::setCoincidenceToAttribute(const AttributePtr& theAttribute,
567                                 const std::set<AttributePoint2DPtr>& theFurtherCoincidences,
568                                 std::set<std::shared_ptr<ModelAPI_Feature>>& theFeaturesToDelete)
569 {
570   FeaturePtr aFeature = ModelAPI_Feature::feature(theAttribute->owner());
571   if (aFeature->getKind() != SketchPlugin_ConstraintCoincidence::ID())
572     return false;
573
574   AttributePoint2DPtr aRefPointAttr = SketchPlugin_ConstraintCoincidence::getPoint(aFeature);
575   if (!aRefPointAttr.get())
576     return false;
577   std::shared_ptr<GeomAPI_Pnt2d> aRefPnt2d = aRefPointAttr->pnt();
578
579   std::set<AttributePoint2DPtr>::const_iterator anIt = theFurtherCoincidences.begin(),
580                                                 aLast = theFurtherCoincidences.end();
581   bool aFoundPoint = false;
582   for (; anIt != aLast && !aFoundPoint; anIt++) {
583     AttributePoint2DPtr aPointAttribute = (*anIt);
584     std::shared_ptr<GeomAPI_Pnt2d> aPoint2d = aPointAttribute->pnt();
585     if (aPoint2d->isEqual(aRefPnt2d)) {
586       // create new coincidence and then remove the old one
587       SketchPlugin_Tools::createConstraintAttrAttr(sketch(),
588           SketchPlugin_ConstraintCoincidence::ID(),
589           aRefPointAttr, aPointAttribute);
590       theFeaturesToDelete.insert(aFeature);
591     }
592   }
593   return aFoundPoint;
594 }
595
596 bool SketchPlugin_Trim::moveTangency(const AttributePtr& theAttribute,
597                                      const FeaturePtr& theFeature)
598 {
599   FeaturePtr aFeature = ModelAPI_Feature::feature(theAttribute->owner());
600   if (aFeature->getKind() != SketchPlugin_ConstraintTangent::ID())
601     return false;
602
603   AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
604                                                                            theAttribute);
605   if (!aRefAttr.get())
606     return false;
607
608   // get shape of tangent object to the current
609   std::string aTangentAttr = SketchPlugin_Constraint::ENTITY_A();
610   if (aRefAttr->id() == SketchPlugin_Constraint::ENTITY_A())
611     aTangentAttr = SketchPlugin_Constraint::ENTITY_B();
612   AttributeRefAttrPtr aTangentRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(
613                                                      aFeature->attribute(aTangentAttr));
614   FeaturePtr aTangentFeature = ModelAPI_Feature::feature(aTangentRefAttr->object());
615
616   // get shape of the feature of the attribute
617   FeaturePtr anAttributeFeature = ModelAPI_Feature::feature(aRefAttr->object());
618   anAttributeFeature->execute(); // the modified value should be applyed to recompute shape
619   GeomAlgoAPI_ShapeTools::PointToRefsMap aPointToAttributeOrObject;
620   std::list<FeaturePtr> aFeatures;
621   aFeatures.push_back(anAttributeFeature);
622   ModelGeomAlgo_Point2D::getPointsIntersectedShape(aTangentFeature, aFeatures,
623                                                    aPointToAttributeOrObject);
624   if (!aPointToAttributeOrObject.empty())
625     return true; // the attribute feature has a point of intersection, so we do not replace it
626
627   // get shape of the feature
628   aPointToAttributeOrObject.clear();
629   aFeatures.clear();
630   aFeatures.push_back(theFeature);
631   ModelGeomAlgo_Point2D::getPointsIntersectedShape(aTangentFeature, aFeatures,
632                                                    aPointToAttributeOrObject);
633   if (!aPointToAttributeOrObject.empty()) {
634     std::set<ResultPtr> anEdgeShapes;
635     ModelGeomAlgo_Shape::shapesOfType(theFeature, GeomAPI_Shape::EDGE, anEdgeShapes);
636     if (!anEdgeShapes.empty()) {
637       ResultPtr aResult = *anEdgeShapes.begin();
638       if (aResult.get()) {
639         aRefAttr->setObject(aResult);
640         return true; // the attribute feature has a point of intersection, so we do not replace it
641       }
642     }
643   }
644   return false;
645 }
646
647 AISObjectPtr SketchPlugin_Trim::getAISObject(AISObjectPtr thePrevious)
648 {
649   return SketchPlugin_SegmentationTools::getAISObject(thePrevious,
650       this, PREVIEW_OBJECT(), PREVIEW_POINT(), SELECTED_OBJECT(), SELECTED_POINT());
651 }
652
653 void SketchPlugin_Trim::getConstraints(std::set<FeaturePtr>& theFeaturesToDelete,
654                                        std::set<FeaturePtr>& theFeaturesToUpdate)
655 {
656   std::shared_ptr<ModelAPI_Data> aData = data();
657
658   // Check the base objects are initialized.
659   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
660                                          aData->attribute(SketchPlugin_Trim::SELECTED_OBJECT()));
661   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
662   ResultPtr aBaseFeatureResult = aBaseFeature->lastResult();
663
664   std::set<AttributePtr> aRefsList = aBaseFeatureResult->data()->refsToMe();
665   std::set<AttributePtr> aFRefsList = aBaseFeature->data()->refsToMe();
666   aRefsList.insert(aFRefsList.begin(), aFRefsList.end());
667
668   std::set<AttributePtr>::const_iterator aIt;
669   for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
670     std::shared_ptr<ModelAPI_Attribute> anAttr = (*aIt);
671     FeaturePtr aRefFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anAttr->owner());
672     std::string aRefFeatureKind = aRefFeature->getKind();
673     std::string anAttributeId = anAttr->id();
674     if ((aRefFeatureKind == SketchPlugin_ConstraintMirror::ID() &&
675          anAttributeId == SketchPlugin_ConstraintMirror::MIRROR_LIST_ID()) ||
676         (aRefFeatureKind == SketchPlugin_MultiRotation::ID() &&
677          anAttributeId == SketchPlugin_MultiRotation::ROTATION_LIST_ID()) ||
678         (aRefFeatureKind == SketchPlugin_MultiTranslation::ID() &&
679          anAttributeId == SketchPlugin_MultiTranslation::TRANSLATION_LIST_ID()) ||
680         aRefFeatureKind == SketchPlugin_ConstraintMiddle::ID())
681       theFeaturesToDelete.insert(aRefFeature);
682     else if (aRefFeatureKind == SketchPlugin_ConstraintLength::ID())
683       theFeaturesToUpdate.insert(aRefFeature);
684   }
685 }
686
687 void SketchPlugin_Trim::removeReferencesToAttribute(const AttributePtr& theAttribute,
688                   std::map<AttributePtr, std::list<AttributePtr> >& theBaseRefAttributes)
689 {
690   /// not found in references
691   if (theBaseRefAttributes.find(theAttribute) == theBaseRefAttributes.end())
692     return;
693
694   std::list<AttributePtr> aRefAttributes = theBaseRefAttributes.at(theAttribute);
695   std::list<AttributePtr>::const_iterator aRefIt = aRefAttributes.begin(),
696                                           aRLast = aRefAttributes.end();
697
698   std::set<FeaturePtr> aFeaturesToDelete;
699   for (; aRefIt != aRLast; aRefIt++) {
700     AttributeRefAttrPtr aRefAttr = std::dynamic_pointer_cast<ModelAPI_AttributeRefAttr>(*aRefIt);
701     if (aRefAttr.get()) {
702       aFeaturesToDelete.insert(ModelAPI_Feature::feature(aRefAttr->owner()));
703     }
704   }
705
706 #ifdef DEBUG_TRIM
707   // delete constraints
708   if (aFeaturesToDelete.size() > 0) {
709     std::cout << "removeReferencesToAttribute: " << std::endl;
710     std::string aValue;
711     for (std::set<FeaturePtr>::const_iterator anIt = aFeaturesToDelete.begin();
712          anIt != aFeaturesToDelete.end(); anIt++) {
713       FeaturePtr aFeature = *anIt;
714       std::cout << aFeature->data()->name() << std::endl;
715     }
716   }
717 #endif
718   ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToDelete);
719   Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_DELETED));
720 }
721
722 FeaturePtr SketchPlugin_Trim::trimLine(const std::shared_ptr<GeomAPI_Pnt2d>& theStartShapePoint,
723                   const std::shared_ptr<GeomAPI_Pnt2d>& theLastShapePoint,
724                   std::map<AttributePtr, std::list<AttributePtr> >& theBaseRefAttributes,
725                   std::set<AttributePoint2DPtr>& thePoints,
726                   std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
727 {
728   FeaturePtr anNewFeature;
729
730   // Check the base objects are initialized.
731   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
732                                         data()->attribute(SketchPlugin_Trim::SELECTED_OBJECT()));
733   ObjectPtr aBaseObject = aBaseObjectAttr->value();
734   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
735
736   /// points of trim
737   AttributePoint2DPtr aStartPointAttrOfBase, anEndPointAttrOfBase;
738   SketchPlugin_SegmentationTools::getFeaturePoints(
739       aBaseFeature, aStartPointAttrOfBase, anEndPointAttrOfBase);
740
741   std::shared_ptr<GeomAPI_Pnt2d> aStartFeaturePoint = aStartPointAttrOfBase->pnt();
742   std::shared_ptr<GeomAPI_Pnt2d> aLastFeaturePoint = anEndPointAttrOfBase->pnt();
743
744   std::shared_ptr<GeomAPI_Pnt2d> aStartShapePoint = theStartShapePoint;
745   std::shared_ptr<GeomAPI_Pnt2d> aLastShapePoint = theLastShapePoint;
746   arrangePointsOnLine(aStartPointAttrOfBase, anEndPointAttrOfBase,
747                       aStartShapePoint, aLastShapePoint);
748 #ifdef DEBUG_TRIM
749   std::cout << "Arranged points (to build split between 1st and 2nd points:" << std::endl;
750   if (aStartShapePoint.get())
751     std::cout << "Start point: [" << aStartShapePoint->x() << ", " <<
752                                        aStartShapePoint->y() << "]" << std::endl;
753   std::cout << "1st point:   [" << aStartFeaturePoint->x() << ", " <<
754                                    aStartFeaturePoint->y() << "]" << std::endl;
755   if (aLastShapePoint.get())
756     std::cout << "2st point:   [" << aLastShapePoint->x() << ", " <<
757                                      aLastShapePoint->y() << "]" << std::endl;
758   std::cout << "End point:   [" << aLastFeaturePoint->x() << ", " <<
759                                    aLastFeaturePoint->y() << "]" << std::endl;
760 #endif
761
762   bool isStartPoint = !aStartShapePoint.get() || aStartFeaturePoint->isEqual(aStartShapePoint);
763   bool isLastPoint = !aLastShapePoint.get() || aLastFeaturePoint->isEqual(aLastShapePoint);
764   if (isStartPoint || isLastPoint) {
765     // result is one line: changed existing line
766     std::string aModifiedAttribute = isStartPoint ? SketchPlugin_Line::START_ID()
767                                                   : SketchPlugin_Line::END_ID();
768     std::shared_ptr<GeomAPI_Pnt2d> aPoint;
769     if (aStartShapePoint.get() && aLastShapePoint.get())
770       aPoint = isStartPoint ? aLastShapePoint : aStartShapePoint;
771     else
772       aPoint = aStartShapePoint.get() ? aStartShapePoint : aLastShapePoint;
773
774     // it is important to delete references before the feature modification because
775     // if deletion will be after the feature modification, solver returns the feature back
776     removeReferencesToAttribute(aBaseFeature->attribute(aModifiedAttribute),
777                                 theBaseRefAttributes);
778
779     fillPointAttribute(aBaseFeature->attribute(aModifiedAttribute), aPoint);
780     //theModifiedAttributes.insert(
781     //  std::make_pair(aBaseFeature->attribute(aModifiedAttribute), AttributePtr()));
782
783     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
784                                (aBaseFeature->attribute(aModifiedAttribute)));
785   }
786   else {
787     // result is two lines: start line point - start shape point,
788     // last shape point - last line point
789     // create second line
790     anNewFeature = SketchPlugin_SegmentationTools::createLineFeature(
791         aBaseFeature, aLastShapePoint, aLastFeaturePoint);
792     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
793                                (anNewFeature->attribute(SketchPlugin_Line::START_ID())));
794
795     std::string aModifiedAttribute = SketchPlugin_Line::END_ID();
796     theModifiedAttributes.insert(
797       std::make_pair(aBaseFeature->attribute(aModifiedAttribute),
798                                    anNewFeature->attribute(SketchPlugin_Line::END_ID())));
799
800     // modify base arc
801     fillPointAttribute(aBaseFeature->attribute(aModifiedAttribute), aStartShapePoint);
802
803     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
804                                (aBaseFeature->attribute(aModifiedAttribute)));
805
806     // Collinear constraint for lines
807     SketchPlugin_Tools::createConstraintObjectObject(sketch(),
808                                          SketchPlugin_ConstraintCollinear::ID(),
809                                          aBaseFeature->lastResult(),
810                                          anNewFeature->lastResult());
811   }
812   return anNewFeature;
813 }
814
815 FeaturePtr SketchPlugin_Trim::trimArc(const std::shared_ptr<GeomAPI_Pnt2d>& theStartShapePoint,
816                  const std::shared_ptr<GeomAPI_Pnt2d>& theLastShapePoint,
817                  std::map<AttributePtr, std::list<AttributePtr> >& theBaseRefAttributes,
818                  std::set<AttributePoint2DPtr>& thePoints,
819                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
820 {
821   FeaturePtr anNewFeature;
822   // Check the base objects are initialized.
823   AttributeReferencePtr aBaseObjectAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
824                                         data()->attribute(SketchPlugin_Trim::SELECTED_OBJECT()));
825   ObjectPtr aBaseObject = aBaseObjectAttr->value();
826   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
827
828   /// points of trim
829   AttributePoint2DPtr aStartPointAttrOfBase, anEndPointAttrOfBase;
830   SketchPlugin_SegmentationTools::getFeaturePoints(
831       aBaseFeature, aStartPointAttrOfBase, anEndPointAttrOfBase);
832
833   std::shared_ptr<GeomAPI_Pnt2d> aStartArcPoint = aStartPointAttrOfBase->pnt();
834   std::shared_ptr<GeomAPI_Pnt2d> aLastArcPoint = anEndPointAttrOfBase->pnt();
835
836   std::shared_ptr<GeomAPI_Pnt2d> aStartShapePoint = theStartShapePoint;
837   std::shared_ptr<GeomAPI_Pnt2d> aLastShapePoint = theLastShapePoint;
838   arrangePointsOnArc(aBaseFeature, aStartPointAttrOfBase, anEndPointAttrOfBase,
839                      aStartShapePoint, aLastShapePoint);
840 #ifdef DEBUG_TRIM
841   std::cout << "Arranged points (to build split between 1st and 2nd points:" << std::endl;
842   if (aStartShapePoint.get())
843     std::cout << "Start shape point: [" << aStartShapePoint->x() << ", " <<
844                                        aStartShapePoint->y() << "]" << std::endl;
845   std::cout << "Start arc attribute point:   [" << aStartArcPoint->x() << ", " <<
846                                    aStartArcPoint->y() << "]" << std::endl;
847   if (aLastShapePoint.get())
848     std::cout << "Last shape point:   [" << aLastShapePoint->x() << ", " <<
849                                      aLastShapePoint->y() << "]" << std::endl;
850   std::cout << "Last arc attribute point:   [" << aLastArcPoint->x() << ", " <<
851                                    aLastArcPoint->y() << "]" << std::endl;
852 #endif
853
854   bool isStartPoint = !aStartShapePoint.get() || aStartArcPoint->isEqual(aStartShapePoint);
855   bool isLastPoint = !aLastShapePoint.get() || aLastArcPoint->isEqual(aLastShapePoint);
856   if (isStartPoint || isLastPoint) {
857     // result is one arc: changed existing arc
858     std::string aModifiedAttribute = isStartPoint ? SketchPlugin_Arc::START_ID()
859                                                   : SketchPlugin_Arc::END_ID();
860     std::shared_ptr<GeomAPI_Pnt2d> aPoint;
861     if (aStartShapePoint.get() && aLastShapePoint.get())
862       aPoint = isStartPoint ? aLastShapePoint : aStartShapePoint;
863     else
864       aPoint = aStartShapePoint.get() ? aStartShapePoint : aLastShapePoint;
865
866     removeReferencesToAttribute(aBaseFeature->attribute(aModifiedAttribute),
867                                 theBaseRefAttributes);
868
869     fillPointAttribute(aBaseFeature->attribute(aModifiedAttribute), aPoint);
870
871     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
872                                (aBaseFeature->attribute(aModifiedAttribute)));
873   }
874   else {
875     // result is two arcs: start arc point - start shape point, last shape point - last arc point
876     // create second arc
877     anNewFeature = SketchPlugin_SegmentationTools::createArcFeature(
878         aBaseFeature, aLastShapePoint, aLastArcPoint);
879     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
880                                (anNewFeature->attribute(SketchPlugin_Arc::START_ID())));
881
882     std::string aModifiedAttribute = SketchPlugin_Arc::END_ID();
883     theModifiedAttributes.insert(
884       std::make_pair(aBaseFeature->attribute(aModifiedAttribute),
885                                    anNewFeature->attribute(SketchPlugin_Arc::END_ID())));
886
887     // modify base arc
888     fillPointAttribute(aBaseFeature->attribute(aModifiedAttribute), aStartShapePoint);
889
890     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
891                                (aBaseFeature->attribute(aModifiedAttribute)));
892
893     // equal Radius constraint for arcs
894     SketchPlugin_Tools::createConstraintObjectObject(sketch(),
895                                          SketchPlugin_ConstraintEqual::ID(),
896                                          aBaseFeature->lastResult(),
897                                          anNewFeature->lastResult());
898     // coincident centers constraint
899     SketchPlugin_Tools::createConstraintAttrAttr(sketch(),
900                                          SketchPlugin_ConstraintCoincidence::ID(),
901                                          aBaseFeature->attribute(SketchPlugin_Arc::CENTER_ID()),
902                                          anNewFeature->attribute(SketchPlugin_Arc::CENTER_ID()));
903
904 #ifdef DEBUG_TRIM
905     std::cout << "Created arc on points:" << std::endl;
906     std::cout << "Start shape point: [" << aStartShapePoint->x() << ", " <<
907                                            aStartShapePoint->y() << "]" << std::endl;
908 #endif
909   }
910   return anNewFeature;
911 }
912
913 FeaturePtr SketchPlugin_Trim::trimEllipticArc(
914                  const std::shared_ptr<GeomAPI_Pnt2d>& theStartShapePoint,
915                  const std::shared_ptr<GeomAPI_Pnt2d>& theLastShapePoint,
916                  std::map<AttributePtr, std::list<AttributePtr> >& theBaseRefAttributes,
917                  std::set<AttributePoint2DPtr>& thePoints,
918                  std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
919 {
920   FeaturePtr anNewFeature;
921   // Check the base objects are initialized.
922   AttributeReferencePtr aBaseObjectAttr = reference(SELECTED_OBJECT());
923   ObjectPtr aBaseObject = aBaseObjectAttr->value();
924   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
925
926   // points of trim
927   AttributePoint2DPtr aStartPointAttrOfBase, anEndPointAttrOfBase;
928   SketchPlugin_SegmentationTools::getFeaturePoints(
929       aBaseFeature, aStartPointAttrOfBase, anEndPointAttrOfBase);
930
931   std::shared_ptr<GeomAPI_Pnt2d> aStartArcPoint = aStartPointAttrOfBase->pnt();
932   std::shared_ptr<GeomAPI_Pnt2d> aLastArcPoint = anEndPointAttrOfBase->pnt();
933
934   std::shared_ptr<GeomAPI_Pnt2d> aStartShapePoint = theStartShapePoint;
935   std::shared_ptr<GeomAPI_Pnt2d> aLastShapePoint = theLastShapePoint;
936   arrangePointsOnArc(aBaseFeature, aStartPointAttrOfBase, anEndPointAttrOfBase,
937                      aStartShapePoint, aLastShapePoint);
938 #ifdef DEBUG_TRIM
939   std::cout << "Arranged points (to build split between 1st and 2nd points:" << std::endl;
940   if (aStartShapePoint.get())
941     std::cout << "Start shape point: [" << aStartShapePoint->x() << ", " <<
942                                        aStartShapePoint->y() << "]" << std::endl;
943   std::cout << "Start arc attribute point:   [" << aStartArcPoint->x() << ", " <<
944                                    aStartArcPoint->y() << "]" << std::endl;
945   if (aLastShapePoint.get())
946     std::cout << "Last shape point:   [" << aLastShapePoint->x() << ", " <<
947                                      aLastShapePoint->y() << "]" << std::endl;
948   std::cout << "Last arc attribute point:   [" << aLastArcPoint->x() << ", " <<
949                                    aLastArcPoint->y() << "]" << std::endl;
950 #endif
951
952   bool isStartPoint = !aStartShapePoint.get() || aStartArcPoint->isEqual(aStartShapePoint);
953   bool isLastPoint = !aLastShapePoint.get() || aLastArcPoint->isEqual(aLastShapePoint);
954   if (isStartPoint || isLastPoint) {
955     // result is one arc: changed existing arc
956     std::string aModifiedAttribute = isStartPoint ? SketchPlugin_EllipticArc::START_POINT_ID()
957                                                   : SketchPlugin_EllipticArc::END_POINT_ID();
958     std::shared_ptr<GeomAPI_Pnt2d> aPoint;
959     if (aStartShapePoint.get() && aLastShapePoint.get())
960       aPoint = isStartPoint ? aLastShapePoint : aStartShapePoint;
961     else
962       aPoint = aStartShapePoint.get() ? aStartShapePoint : aLastShapePoint;
963
964     removeReferencesToAttribute(aBaseFeature->attribute(aModifiedAttribute),
965                                 theBaseRefAttributes);
966
967     fillPointAttribute(aBaseFeature->attribute(aModifiedAttribute), aPoint);
968
969     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
970                                (aBaseFeature->attribute(aModifiedAttribute)));
971   }
972   else {
973     // result is two arcs: start arc point - start shape point, last shape point - last arc point
974     // create second arc
975     anNewFeature = SketchPlugin_SegmentationTools::createArcFeature(
976         aBaseFeature, aLastShapePoint, aLastArcPoint);
977     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
978                      anNewFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID())));
979
980     std::string aModifiedAttribute = SketchPlugin_EllipticArc::END_POINT_ID();
981     theModifiedAttributes.insert(
982       std::make_pair(aBaseFeature->attribute(aModifiedAttribute),
983                      anNewFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID())));
984
985     // modify base arc
986     fillPointAttribute(aBaseFeature->attribute(aModifiedAttribute), aStartShapePoint);
987
988     thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
989                                (aBaseFeature->attribute(aModifiedAttribute)));
990
991     // make elliptic arcs equal
992     SketchPlugin_Tools::createConstraintObjectObject(sketch(),
993                                          SketchPlugin_ConstraintEqual::ID(),
994                                          aBaseFeature->lastResult(),
995                                          anNewFeature->lastResult());
996     // coincident centers constraint
997     SketchPlugin_Tools::createConstraintAttrAttr(sketch(),
998         SketchPlugin_ConstraintCoincidence::ID(),
999         aBaseFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()),
1000         anNewFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()));
1001
1002 #ifdef DEBUG_TRIM
1003     std::cout << "Created arc on points:" << std::endl;
1004     std::cout << "Start shape point: [" << aStartShapePoint->x() << ", " <<
1005                                            aStartShapePoint->y() << "]" << std::endl;
1006 #endif
1007   }
1008   return anNewFeature;
1009 }
1010
1011 FeaturePtr SketchPlugin_Trim::trimClosed(const std::shared_ptr<GeomAPI_Pnt2d>& theStartShapePoint,
1012                                          const std::shared_ptr<GeomAPI_Pnt2d>& theLastShapePoint,
1013                                          std::set<AttributePoint2DPtr>& thePoints,
1014                            std::set<std::pair<AttributePtr, AttributePtr>>& theModifiedAttributes)
1015 {
1016   // Check the base objects are initialized.
1017   AttributeReferencePtr aBaseObjectAttr = reference(SELECTED_OBJECT());
1018   ObjectPtr aBaseObject = aBaseObjectAttr->value();
1019   FeaturePtr aBaseFeature = ModelAPI_Feature::feature(aBaseObjectAttr->value());
1020
1021   // trim feature
1022   FeaturePtr anNewFeature = SketchPlugin_SegmentationTools::createArcFeature(
1023       aBaseFeature, theStartShapePoint, theLastShapePoint);
1024   // arc created by trim of circle is always correct, that means that it is not inversed
1025   const std::string& aReversedAttrName = anNewFeature->getKind() == SketchPlugin_Arc::ID() ?
1026       SketchPlugin_Arc::REVERSED_ID() : SketchPlugin_EllipticArc::REVERSED_ID();
1027   anNewFeature->boolean(aReversedAttrName)->setValue(false);
1028
1029   if (aBaseFeature->getKind() == SketchPlugin_Circle::ID()) {
1030     theModifiedAttributes.insert(
1031       std::make_pair(aBaseFeature->attribute(SketchPlugin_Circle::CENTER_ID()),
1032                      anNewFeature->attribute(SketchPlugin_Arc::CENTER_ID())));
1033   }
1034   else if (aBaseFeature->getKind() == SketchPlugin_Ellipse::ID()) {
1035     theModifiedAttributes.insert(std::make_pair(
1036         aBaseFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()),
1037         anNewFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID())));
1038     theModifiedAttributes.insert(std::make_pair(
1039         aBaseFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()),
1040         anNewFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID())));
1041     theModifiedAttributes.insert(std::make_pair(
1042         aBaseFeature->attribute(SketchPlugin_Ellipse::SECOND_FOCUS_ID()),
1043         anNewFeature->attribute(SketchPlugin_EllipticArc::SECOND_FOCUS_ID())));
1044     theModifiedAttributes.insert(std::make_pair(
1045         aBaseFeature->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_START_ID()),
1046         anNewFeature->attribute(SketchPlugin_EllipticArc::MAJOR_AXIS_START_ID())));
1047     theModifiedAttributes.insert(std::make_pair(
1048         aBaseFeature->attribute(SketchPlugin_Ellipse::MAJOR_AXIS_END_ID()),
1049         anNewFeature->attribute(SketchPlugin_EllipticArc::MAJOR_AXIS_END_ID())));
1050     theModifiedAttributes.insert(std::make_pair(
1051         aBaseFeature->attribute(SketchPlugin_Ellipse::MINOR_AXIS_START_ID()),
1052         anNewFeature->attribute(SketchPlugin_EllipticArc::MINOR_AXIS_START_ID())));
1053     theModifiedAttributes.insert(std::make_pair(
1054         aBaseFeature->attribute(SketchPlugin_Ellipse::MINOR_AXIS_END_ID()),
1055         anNewFeature->attribute(SketchPlugin_EllipticArc::MINOR_AXIS_END_ID())));
1056
1057     // update the PARENT_ID reference for all the features created by the ellipse
1058     const std::set<AttributePtr>& aRefs = aBaseFeature->data()->refsToMe();
1059     std::list<AttributePtr> aRefsToParent;
1060     for (std::set<AttributePtr>::const_iterator aRef = aRefs.begin(); aRef != aRefs.end(); ++aRef) {
1061       if ((*aRef)->id() == SketchPlugin_SketchEntity::PARENT_ID())
1062         aRefsToParent.push_back(*aRef);
1063     }
1064     for (std::list<AttributePtr>::iterator aRef = aRefsToParent.begin();
1065          aRef != aRefsToParent.end(); ++aRef) {
1066       std::dynamic_pointer_cast<ModelAPI_AttributeReference>(*aRef)->setValue(anNewFeature);
1067
1068       FeaturePtr anOwner = ModelAPI_Feature::feature((*aRef)->owner());
1069       SketchPlugin_Tools::replaceInName(anOwner, aBaseFeature->name(), anNewFeature->name());
1070       SketchPlugin_Tools::replaceInName(anOwner->lastResult(),
1071           aBaseFeature->name(), anNewFeature->name());
1072     }
1073   }
1074
1075   const std::string& aStartAttrName = anNewFeature->getKind() == SketchPlugin_Arc::ID() ?
1076       SketchPlugin_Arc::START_ID() : SketchPlugin_EllipticArc::START_POINT_ID();
1077   const std::string& aEndAttrName = anNewFeature->getKind() == SketchPlugin_Arc::ID() ?
1078       SketchPlugin_Arc::END_ID() : SketchPlugin_EllipticArc::END_POINT_ID();
1079
1080   thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
1081                              (anNewFeature->attribute(aStartAttrName)));
1082   thePoints.insert(std::dynamic_pointer_cast<GeomDataAPI_Point2D>
1083                              (anNewFeature->attribute(aEndAttrName)));
1084
1085   return anNewFeature;
1086 }
1087
1088 void SketchPlugin_Trim::arrangePointsOnLine(const AttributePoint2DPtr& theStartPointAttr,
1089                                             const AttributePoint2DPtr& /*theEndPointAttr*/,
1090                                             std::shared_ptr<GeomAPI_Pnt2d>& theFirstPoint,
1091                                             std::shared_ptr<GeomAPI_Pnt2d>& theLastPoint) const
1092 {
1093   if (!theFirstPoint.get() || !theLastPoint.get())
1094     return;
1095
1096   // if first point is closer to last point, swap first and last values
1097   if (theStartPointAttr->pnt()->distance(theFirstPoint) >
1098       theStartPointAttr->pnt()->distance(theLastPoint)) {
1099     std::shared_ptr<GeomAPI_Pnt2d> aTmpPoint = theFirstPoint;
1100     theFirstPoint = theLastPoint;
1101     theLastPoint = aTmpPoint;
1102   }
1103 }
1104
1105 void SketchPlugin_Trim::arrangePointsOnArc(const FeaturePtr& theArc,
1106                                   const AttributePoint2DPtr& theStartPointAttr,
1107                                   const AttributePoint2DPtr& /*theEndPointAttr*/,
1108                                   std::shared_ptr<GeomAPI_Pnt2d>& theFirstPoint,
1109                                   std::shared_ptr<GeomAPI_Pnt2d>& theSecondPoint) const
1110 {
1111   if (!theFirstPoint.get() || !theSecondPoint.get())
1112     return;
1113
1114   static const double anAngleTol = 1.e-12;
1115
1116   const std::string& aCenterAttrName = theArc->getKind() == SketchPlugin_Arc::ID() ?
1117       SketchPlugin_Arc::CENTER_ID() : SketchPlugin_EllipticArc::CENTER_ID();
1118   const std::string& aReversedAttrName = theArc->getKind() == SketchPlugin_Arc::ID() ?
1119       SketchPlugin_Arc::REVERSED_ID() : SketchPlugin_EllipticArc::REVERSED_ID();
1120
1121   std::shared_ptr<GeomAPI_Pnt2d> aCenter = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1122       theArc->attribute(aCenterAttrName))->pnt();
1123   bool isReversed = theArc->boolean(aReversedAttrName)->value();
1124
1125   // collect directions to each point
1126   std::shared_ptr<GeomAPI_Dir2d> aStartDir(
1127       new GeomAPI_Dir2d(theStartPointAttr->pnt()->xy()->decreased(aCenter->xy())));
1128   std::shared_ptr<GeomAPI_Dir2d> aFirstPtDir(
1129       new GeomAPI_Dir2d(theFirstPoint->xy()->decreased(aCenter->xy())));
1130   std::shared_ptr<GeomAPI_Dir2d> aSecondPtDir(
1131       new GeomAPI_Dir2d(theSecondPoint->xy()->decreased(aCenter->xy())));
1132
1133   // sort points by their angular values
1134   double aFirstPtAngle = aStartDir->angle(aFirstPtDir);
1135   double aSecondPtAngle = aStartDir->angle(aSecondPtDir);
1136   double aPeriod = isReversed ? -2.0 * PI : 2.0 * PI;
1137   if (fabs(aFirstPtAngle) > anAngleTol && isReversed == (aFirstPtAngle > 0.))
1138     aFirstPtAngle += aPeriod;
1139   if (fabs(aSecondPtAngle) > anAngleTol && isReversed == (aSecondPtAngle > 0.))
1140     aSecondPtAngle += aPeriod;
1141
1142   if (fabs(aFirstPtAngle) > fabs(aSecondPtAngle)) {
1143     std::shared_ptr<GeomAPI_Pnt2d> aTmpPoint = theFirstPoint;
1144     theFirstPoint = theSecondPoint;
1145     theSecondPoint = aTmpPoint;
1146   }
1147 }
1148
1149 void SketchPlugin_Trim::fillPointAttribute(const AttributePtr& theModifiedAttribute,
1150                                            const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
1151 {
1152   std::string anAttributeType = theModifiedAttribute->attributeType();
1153   if (anAttributeType == GeomDataAPI_Point2D::typeId()) {
1154     AttributePoint2DPtr aModifiedAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1155                                               theModifiedAttribute);
1156     aModifiedAttribute->setValue(thePoint);
1157
1158 #ifdef DEBUG_TRIM
1159     FeaturePtr aFeature = ModelAPI_Feature::feature(theModifiedAttribute->owner());
1160     std::cout << "<fillPointAttribute[" << aFeature->data()->name() << ": " <<
1161       theModifiedAttribute->id() <<
1162       "]> => Pnt2d - [" << thePoint->x() << ", " << thePoint->y() << "]" << std::endl;
1163 #endif
1164   }
1165 }
1166
1167 void SketchPlugin_Trim::fillAttribute(const AttributePtr& theModifiedAttribute,
1168                                       const AttributePtr& theSourceAttribute)
1169 {
1170   std::string anAttributeType = theModifiedAttribute->attributeType();
1171   if (anAttributeType == GeomDataAPI_Point2D::typeId()) {
1172     AttributePoint2DPtr aModifiedAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1173                                               theModifiedAttribute);
1174     AttributePoint2DPtr aSourceAttribute = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
1175                                               theSourceAttribute);
1176
1177     if (aModifiedAttribute.get() && aSourceAttribute.get())
1178       aModifiedAttribute->setValue(aSourceAttribute->pnt());
1179   }
1180   else if (anAttributeType == ModelAPI_AttributeBoolean::typeId()) {
1181     AttributeBooleanPtr aModifiedAttribute = std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(
1182                                               theModifiedAttribute);
1183     AttributeBooleanPtr aSourceAttribute = std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(
1184                                               theSourceAttribute);
1185
1186     if (aModifiedAttribute.get() && aSourceAttribute.get())
1187       aModifiedAttribute->setValue(aSourceAttribute->value());
1188   }
1189 }