Salome HOME
63614449c11b40b2bd277cceb1e42c5908310cbe
[modules/shaper.git] / src / Model / Model_AttributeSelection.cpp
1 // Copyright (C) 2014-2017  CEA/DEN, EDF R&D
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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include "Model_AttributeSelection.h"
22 #include "Model_Application.h"
23 #include "Model_Events.h"
24 #include "Model_Data.h"
25 #include "Model_Document.h"
26 #include "Model_SelectionNaming.h"
27 #include <Model_Objects.h>
28 #include <Model_AttributeSelectionList.h>
29 #include <Model_ResultConstruction.h>
30 #include <ModelAPI_Feature.h>
31 #include <ModelAPI_ResultBody.h>
32 #include <ModelAPI_ResultCompSolid.h>
33 #include <ModelAPI_ResultConstruction.h>
34 #include <ModelAPI_ResultPart.h>
35 #include <ModelAPI_CompositeFeature.h>
36 #include <ModelAPI_Tools.h>
37 #include <ModelAPI_Session.h>
38 #include <Events_InfoMessage.h>
39 #include <GeomAPI_Edge.h>
40 #include <GeomAPI_Vertex.h>
41
42 #include <TNaming_Selector.hxx>
43 #include <TNaming_NamedShape.hxx>
44 #include <TNaming_Tool.hxx>
45 #include <TNaming_Builder.hxx>
46 #include <TNaming_SameShapeIterator.hxx>
47 #include <TNaming_Iterator.hxx>
48 #include <TDataStd_Integer.hxx>
49 #include <TDataStd_UAttribute.hxx>
50 #include <TDataStd_Name.hxx>
51 #include <TopTools_ListOfShape.hxx>
52 #include <TopExp_Explorer.hxx>
53 #include <BRep_Tool.hxx>
54 #include <TopoDS.hxx>
55 #include <TopExp.hxx>
56 #include <TDF_ChildIterator.hxx>
57 #include <TDF_ChildIDIterator.hxx>
58 #include <TopoDS_Iterator.hxx>
59 #include <Geom_Circle.hxx>
60 #include <Geom_Ellipse.hxx>
61 #include <BRep_Builder.hxx>
62
63 //#define DEB_NAMING 1
64 #ifdef DEB_NAMING
65 #include <BRepTools.hxx>
66 #endif
67 /// added to the index in the packed map to signalize that the vertex of edge is selected
68 /// (multiplied by the index of the edge)
69 static const int kSTART_VERTEX_DELTA = 1000000;
70 // identifier that there is simple reference: selection equals to context
71 Standard_GUID kSIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb29");
72 // reference to Part sub-object
73 Standard_GUID kPART_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb27");
74 // selection is invalid after recomputation
75 Standard_GUID kINVALID_SELECTION("bce47fd7-80fa-4462-9d63-2f58acddd49d");
76
77 // identifier of the selection of the center of circle on edge
78 Standard_GUID kCIRCLE_CENTER("d0d0e0f1-217a-4b95-8fbb-0c4132f23718");
79 // identifier of the selection of the first focus point of ellipse on edge
80 Standard_GUID kELLIPSE_CENTER1("f70df04c-3168-4dc9-87a4-f1f840c1275d");
81 // identifier of the selection of the second focus point of ellipse on edge
82 Standard_GUID kELLIPSE_CENTER2("1395ae73-8e02-4cf8-b204-06ff35873a32");
83
84 // on this label is stored:
85 // TNaming_NamedShape - selected shape
86 // TNaming_Naming - topological selection information (for the body)
87 // TDataStd_IntPackedMap - indexes of edges in composite element (for construction)
88 // TDataStd_Integer - type of the selected shape (for construction)
89 // TDF_Reference - from ReferenceAttribute, the context
90 void Model_AttributeSelection::setValue(const ResultPtr& theContext,
91   const std::shared_ptr<GeomAPI_Shape>& theSubShape, const bool theTemporarily)
92 {
93   if (theTemporarily) { // just keep the stored without DF update
94     myTmpContext = theContext;
95     myTmpSubShape = theSubShape;
96     owner()->data()->sendAttributeUpdated(this);
97     return;
98   } else {
99     myTmpContext.reset();
100     myTmpSubShape.reset();
101     myTmpCenterType = NOT_CENTER;
102   }
103
104   const std::shared_ptr<GeomAPI_Shape>& anOldShape = value();
105   bool isOldContext = theContext == myRef.value();
106   bool isOldShape = isOldContext &&
107     (theSubShape == anOldShape || (theSubShape && anOldShape && theSubShape->isEqual(anOldShape)));
108   if (isOldShape) return; // shape is the same, so context is also unchanged
109   // update the referenced object if needed
110   if (!isOldContext) {
111       myRef.setValue(theContext);
112   }
113
114   // do noth use naming if selected shape is result shape itself, but not sub-shape
115   TDF_Label aSelLab = selectionLabel();
116   aSelLab.ForgetAttribute(kSIMPLE_REF_ID);
117   aSelLab.ForgetAttribute(kINVALID_SELECTION);
118   aSelLab.ForgetAttribute(kCIRCLE_CENTER);
119   aSelLab.ForgetAttribute(kELLIPSE_CENTER1);
120   aSelLab.ForgetAttribute(kELLIPSE_CENTER2);
121
122   bool isDegeneratedEdge = false;
123   // do not use the degenerated edge as a shape, a null context and shape is used in the case
124   if (theSubShape.get() && !theSubShape->isNull() && theSubShape->isEdge()) {
125     const TopoDS_Shape& aSubShape = theSubShape->impl<TopoDS_Shape>();
126     if (aSubShape.ShapeType() == TopAbs_EDGE)
127       isDegeneratedEdge = BRep_Tool::Degenerated(TopoDS::Edge(aSubShape)) == Standard_True;
128   }
129   if (!theContext.get() || isDegeneratedEdge) {
130     // to keep the reference attribute label
131     TDF_Label aRefLab = myRef.myRef->Label();
132     aSelLab.ForgetAllAttributes(true);
133     myRef.myRef = TDF_Reference::Set(aSelLab.Father(), aSelLab.Father());
134     return;
135   }
136   if (theContext->groupName() == ModelAPI_ResultBody::group()) {
137     // do not select the whole shape for body:it is already must be in the data framework
138     // equal and null selected objects mean the same: object is equal to context,
139     if (theContext->shape().get() &&
140         (theContext->shape()->isEqual(theSubShape) || !theSubShape.get())) {
141       aSelLab.ForgetAllAttributes(true);
142       TDataStd_UAttribute::Set(aSelLab, kSIMPLE_REF_ID);
143     } else {
144       selectBody(theContext, theSubShape);
145     }
146   } else if (theContext->groupName() == ModelAPI_ResultConstruction::group()) {
147     aSelLab.ForgetAllAttributes(true); // to remove old selection data
148     std::shared_ptr<Model_ResultConstruction> aConstruction =
149       std::dynamic_pointer_cast<Model_ResultConstruction>(theContext);
150     std::shared_ptr<GeomAPI_Shape> aSubShape;
151     if (theSubShape.get() && !theContext->shape()->isEqual(theSubShape))
152       aSubShape = theSubShape; // the whole context
153     int anIndex = aConstruction->select(theSubShape, owner()->document());
154     TDataStd_Integer::Set(aSelLab, anIndex);
155   } else if (theContext->groupName() == ModelAPI_ResultPart::group()) {
156     aSelLab.ForgetAllAttributes(true);
157     TDataStd_UAttribute::Set(aSelLab, kPART_REF_ID);
158     selectPart(theContext, theSubShape);
159   }
160
161   owner()->data()->sendAttributeUpdated(this);
162 }
163
164 void Model_AttributeSelection::setValueCenter(
165     const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Edge>& theEdge,
166     const CenterType theCenterType, const bool theTemporarily)
167 {
168   setValue(theContext, theEdge, theTemporarily);
169   if (theTemporarily) {
170     myTmpCenterType = theCenterType;
171   } else { // store in the data structure
172     TDF_Label aSelLab = selectionLabel();
173     switch(theCenterType) {
174     case CIRCLE_CENTER:
175       TDataStd_UAttribute::Set(aSelLab, kCIRCLE_CENTER);
176       break;
177     case ELLIPSE_FIRST_FOCUS:
178       TDataStd_UAttribute::Set(aSelLab, kELLIPSE_CENTER1);
179       break;
180     case ELLIPSE_SECOND_FOCUS:
181       TDataStd_UAttribute::Set(aSelLab, kELLIPSE_CENTER2);
182       break;
183     }
184     owner()->data()->sendAttributeUpdated(this);
185   }
186 }
187
188
189 void Model_AttributeSelection::removeTemporaryValues()
190 {
191   if (myTmpContext.get() || myTmpSubShape.get()) {
192     myTmpContext.reset();
193     myTmpSubShape.reset();
194   }
195 }
196
197 // returns the center of the edge: circular or elliptical
198 GeomShapePtr centerByEdge(GeomShapePtr theEdge, ModelAPI_AttributeSelection::CenterType theType)
199 {
200   if (theType != ModelAPI_AttributeSelection::NOT_CENTER && theEdge.get() != NULL) {
201     TopoDS_Shape aShape = theEdge->impl<TopoDS_Shape>();
202     if (!aShape.IsNull() && aShape.ShapeType() == TopAbs_EDGE) {
203       TopoDS_Edge anEdge = TopoDS::Edge(aShape);
204       double aFirst, aLast;
205       Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
206       if (!aCurve.IsNull()) {
207         TopoDS_Vertex aVertex;
208         BRep_Builder aBuilder;
209         if (theType == ModelAPI_AttributeSelection::CIRCLE_CENTER) {
210           Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast(aCurve);
211           if (!aCirc.IsNull()) {
212             aBuilder.MakeVertex(aVertex, aCirc->Location(), Precision::Confusion());
213           }
214         } else { // ellipse
215           Handle(Geom_Ellipse) anEll = Handle(Geom_Ellipse)::DownCast(aCurve);
216           if (!anEll.IsNull()) {
217             aBuilder.MakeVertex(aVertex,
218               theType == ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS ?
219               anEll->Focus1() : anEll->Focus2(), Precision::Confusion());
220           }
221         }
222         if (!aVertex.IsNull()) {
223           std::shared_ptr<GeomAPI_Vertex> aResult(new GeomAPI_Vertex);
224           aResult->setImpl(new TopoDS_Vertex(aVertex));
225           return aResult;
226         }
227       }
228     }
229   }
230   return theEdge; // no vertex, so, return the initial edge
231 }
232
233 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
234 {
235   CenterType aType = NOT_CENTER;
236   std::shared_ptr<GeomAPI_Shape> aResult = internalValue(aType);
237   return centerByEdge(aResult, aType);
238 }
239
240 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::internalValue(CenterType& theType)
241 {
242   theType = NOT_CENTER;
243   GeomShapePtr aResult;
244   if (myTmpContext.get() || myTmpSubShape.get()) {
245     theType = myTmpCenterType;
246     ResultConstructionPtr aResulConstruction =
247       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(myTmpContext);
248     if(aResulConstruction.get()) {
249       // it is just reference to construction.
250       return myTmpSubShape;
251     }
252     return myTmpSubShape.get() ? myTmpSubShape : myTmpContext->shape();
253   }
254
255   TDF_Label aSelLab = selectionLabel();
256   if (aSelLab.IsAttribute(kINVALID_SELECTION))
257     return aResult;
258
259   if (aSelLab.IsAttribute(kCIRCLE_CENTER))
260     theType = CIRCLE_CENTER;
261   else if (aSelLab.IsAttribute(kELLIPSE_CENTER1))
262     theType = ELLIPSE_FIRST_FOCUS;
263   else if (aSelLab.IsAttribute(kELLIPSE_CENTER2))
264     theType = ELLIPSE_SECOND_FOCUS;
265
266
267   if (myRef.isInitialized()) {
268     if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
269       ResultPtr aContext = context();
270       if (!aContext.get())
271         return aResult; // empty result
272       return aContext->shape();
273     }
274     if (aSelLab.IsAttribute(kPART_REF_ID)) {
275       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(context());
276       if (!aPart.get() || !aPart->isActivated())
277         return aResult; // postponed naming needed
278       Handle(TDataStd_Integer) anIndex;
279       if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
280         if (anIndex->Get()) { // special selection attribute was created, use it
281           return aPart->selectionValue(anIndex->Get());
282         } else { // face with name is already in the data model, so try to take it by name
283           Handle(TDataStd_Name) aName;
284           if (aSelLab.FindAttribute(TDataStd_Name::GetID(), aName)) {
285             std::string aSubShapeName(TCollection_AsciiString(aName->Get()).ToCString());
286             std::size_t aPartEnd = aSubShapeName.find('/');
287             if (aPartEnd != std::string::npos && aPartEnd != aSubShapeName.rfind('/')) {
288               std::string aNameInPart = aSubShapeName.substr(aPartEnd + 1);
289               int anIndex;
290               std::string aTypeStr; // to reuse already existing selection the type is not needed
291               return aPart->shapeInPart(aNameInPart, aTypeStr, anIndex);
292             }
293           }
294         }
295       }
296     }
297
298     Handle(TNaming_NamedShape) aSelection;
299     if (aSelLab.FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
300       TopoDS_Shape aSelShape = aSelection->Get();
301       aResult = std::shared_ptr<GeomAPI_Shape>(new GeomAPI_Shape);
302       aResult->setImpl(new TopoDS_Shape(aSelShape));
303       return aResult;
304     } else { // for simple construction element: just shape of this construction element
305       std::shared_ptr<Model_ResultConstruction> aConstr =
306         std::dynamic_pointer_cast<Model_ResultConstruction>(context());
307       if (aConstr) {
308         Handle(TDataStd_Integer) anIndex;
309         if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
310           if (anIndex->Get() == 0) // it is just reference to construction, nothing is in value
311             return aResult;
312           return aConstr->shape(anIndex->Get(), owner()->document());
313         }
314       }
315     }
316   }
317   return aResult; // empty case
318 }
319
320 bool Model_AttributeSelection::isInvalid()
321 {
322   return selectionLabel().IsAttribute(kINVALID_SELECTION) == Standard_True;
323 }
324
325 bool Model_AttributeSelection::isInitialized()
326 {
327   if (ModelAPI_AttributeSelection::isInitialized()) { // additional checks if it is initialized
328     std::shared_ptr<GeomAPI_Shape> aResult;
329     if (myRef.isInitialized()) {
330       TDF_Label aSelLab = selectionLabel();
331       if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
332         ResultPtr aContext = context();
333         return aContext.get() != NULL;
334       }
335       Handle(TNaming_NamedShape) aSelection;
336       if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
337         return !aSelection->Get().IsNull();
338       } else { // for simple construction element: just shape of this construction element
339         std::shared_ptr<Model_ResultConstruction> aConstr =
340           std::dynamic_pointer_cast<Model_ResultConstruction>(context());
341         if (aConstr.get()) {
342           Handle(TDataStd_Integer) anIndex;
343           if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
344             // for the whole shape it may return null, so, if index exists, returns true
345             return true;
346           }
347         }
348       }
349     }
350   }
351   return false;
352 }
353
354 Model_AttributeSelection::Model_AttributeSelection(TDF_Label& theLabel)
355   : myRef(theLabel)
356 {
357   myIsInitialized = myRef.isInitialized();
358   myParent = NULL;
359 }
360
361 void Model_AttributeSelection::setID(const std::string theID)
362 {
363   myRef.setID(theID);
364   ModelAPI_AttributeSelection::setID(theID);
365 }
366
367 ResultPtr Model_AttributeSelection::context() {
368   if (myTmpContext.get() || myTmpSubShape.get()) {
369     return myTmpContext;
370   }
371
372   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
373   // for parts there could be same-data result, so take the last enabled
374   if (aResult.get() && aResult->groupName() == ModelAPI_ResultPart::group()) {
375     int aSize = aResult->document()->size(ModelAPI_ResultPart::group());
376     for(int a = aSize - 1; a >= 0; a--) {
377       ObjectPtr aPart = aResult->document()->object(ModelAPI_ResultPart::group(), a);
378       if (aPart.get() && aPart->data() == aResult->data()) {
379         ResultPtr aPartResult = std::dynamic_pointer_cast<ModelAPI_Result>(aPart);
380         FeaturePtr anOwnerFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
381         // check that this result is not this-feature result (it is forbidden t oselect itself)
382         if (anOwnerFeature.get() && anOwnerFeature->firstResult() != aPartResult) {
383           return aPartResult;
384         }
385       }
386     }
387   }
388   return aResult;
389 }
390
391
392 void Model_AttributeSelection::setObject(const std::shared_ptr<ModelAPI_Object>& theObject)
393 {
394   ModelAPI_AttributeSelection::setObject(theObject);
395   myRef.setObject(theObject);
396 }
397
398 TDF_LabelMap& Model_AttributeSelection::scope()
399 {
400   if (myScope.IsEmpty()) { // create a new scope if not yet done
401     // gets all features with named shapes that are before this feature label (before in history)
402     DocumentPtr aMyDoc = owner()->document();
403     std::list<std::shared_ptr<ModelAPI_Feature> > allFeatures = aMyDoc->allFeatures();
404     std::list<std::shared_ptr<ModelAPI_Feature> >::iterator aFIter = allFeatures.begin();
405     bool aMePassed = false;
406     CompositeFeaturePtr aComposite =
407       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(owner());
408     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
409     CompositeFeaturePtr aCompositeOwner, aCompositeOwnerOwner;
410     if (aFeature.get()) {
411       aCompositeOwner = ModelAPI_Tools::compositeOwner(aFeature);
412       if (aCompositeOwner.get()) {
413          aCompositeOwnerOwner = ModelAPI_Tools::compositeOwner(aCompositeOwner);
414       }
415     }
416     // for group Scope is not limitet: this is always up to date objects
417     bool isGroup = aFeature.get() && aFeature->getKind() == "Group";
418     for(; aFIter != allFeatures.end(); aFIter++) {
419       if (*aFIter == owner()) {  // the left features are created later (except subs of composite)
420         aMePassed = true;
421         continue;
422       }
423       if (isGroup) aMePassed = false;
424       bool isInScope = !aMePassed;
425       if (!isInScope && aComposite.get()) {
426         // try to add sub-elements of composite if this is composite
427         if (aComposite->isSub(*aFIter))
428           isInScope = true;
429       }
430       // remove the composite-owner of this feature (sketch in extrusion-cut)
431       if (isInScope && (aCompositeOwner == *aFIter || aCompositeOwnerOwner == *aFIter))
432         isInScope = false;
433
434       if (isInScope && aFIter->get() && (*aFIter)->data()->isValid()) {
435         TDF_Label aFeatureLab = std::dynamic_pointer_cast<Model_Data>(
436           (*aFIter)->data())->label().Father();
437         TDF_ChildIDIterator aNSIter(aFeatureLab, TNaming_NamedShape::GetID(), true);
438         for(; aNSIter.More(); aNSIter.Next()) {
439           Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
440           if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
441             myScope.Add(aNS->Label());
442           }
443         }
444       }
445     }
446     // also add all naming labels created for external constructions
447     std::shared_ptr<Model_Document> aDoc = std::dynamic_pointer_cast<Model_Document>(aMyDoc);
448     TDF_Label anExtDocLab = aDoc->extConstructionsLabel();
449     TDF_ChildIDIterator aNSIter(anExtDocLab, TNaming_NamedShape::GetID(), true);
450     for(; aNSIter.More(); aNSIter.Next()) {
451       Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
452       if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
453         myScope.Add(aNS->Label());
454       }
455     }
456   }
457   return myScope;
458 }
459
460 /// Sets the invalid flag if flag is false, or removes it if "true"
461 /// Returns theFlag
462 static bool setInvalidIfFalse(TDF_Label& theLab, const bool theFlag) {
463   if (theFlag) {
464     theLab.ForgetAttribute(kINVALID_SELECTION);
465   } else {
466     TDataStd_UAttribute::Set(theLab, kINVALID_SELECTION);
467   }
468   return theFlag;
469 }
470
471 void Model_AttributeSelection::split(
472   ResultPtr theContext, TopoDS_Shape theNewShape, TopAbs_ShapeEnum theType)
473 {
474   TopTools_ListOfShape aSubs;
475   for(TopoDS_Iterator anExplorer(theNewShape); anExplorer.More(); anExplorer.Next()) {
476     if (!anExplorer.Value().IsNull() &&
477       anExplorer.Value().ShapeType() == theType) {
478         aSubs.Append(anExplorer.Value());
479     } else { // invalid case; bad result shape, so, impossible to split easily
480       aSubs.Clear();
481       break;
482     }
483   }
484   if (aSubs.Extent() > 1) { // ok to split
485     TopTools_ListIteratorOfListOfShape aSub(aSubs);
486     GeomShapePtr aSubSh(new GeomAPI_Shape);
487     aSubSh->setImpl(new TopoDS_Shape(aSub.Value()));
488     setValue(theContext, aSubSh);
489     for(aSub.Next(); aSub.More(); aSub.Next()) {
490       GeomShapePtr aSubSh(new GeomAPI_Shape);
491       aSubSh->setImpl(new TopoDS_Shape(aSub.Value()));
492       myParent->append(theContext, aSubSh);
493     }
494   }
495 }
496
497 bool Model_AttributeSelection::update()
498 {
499   TDF_Label aSelLab = selectionLabel();
500   ResultPtr aContext = context();
501   if (!aContext.get())
502     return setInvalidIfFalse(aSelLab, false);
503   if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
504     return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull());
505   }
506
507   if (aSelLab.IsAttribute(kPART_REF_ID)) { // it is reference to the part object
508     std::shared_ptr<GeomAPI_Shape> aNoSelection;
509     bool aResult = selectPart(aContext, aNoSelection, true);
510     aResult = setInvalidIfFalse(aSelLab, aResult);
511     if (aResult) {
512       owner()->data()->sendAttributeUpdated(this);
513     }
514     return aResult;
515   }
516
517   if (aContext->groupName() == ModelAPI_ResultBody::group()) {
518     // body: just a named shape, use selection mechanism from OCCT
519     TNaming_Selector aSelector(aSelLab);
520     TopoDS_Shape anOldShape;
521     if (!aSelector.NamedShape().IsNull()) {
522       anOldShape = aSelector.NamedShape()->Get();
523     }
524     bool aResult = aSelector.Solve(scope()) == Standard_True;
525     // must be before sending of updated attribute (1556)
526     aResult = setInvalidIfFalse(aSelLab, aResult);
527     TopoDS_Shape aNewShape;
528     if (!aSelector.NamedShape().IsNull()) {
529       aNewShape = aSelector.NamedShape()->Get();
530     }
531     if (anOldShape.IsNull() || aNewShape.IsNull() ||
532         !anOldShape.IsEqual(aSelector.NamedShape()->Get())) {
533       // shape type shoud not not changed: if shape becomes compound of such shapes, then split
534       if (myParent && !anOldShape.IsNull() && !aNewShape.IsNull() &&
535           anOldShape.ShapeType() != aNewShape.ShapeType() &&
536           (aNewShape.ShapeType() == TopAbs_COMPOUND || aNewShape.ShapeType() == TopAbs_COMPSOLID))
537       {
538         split(aContext, aNewShape, anOldShape.ShapeType());
539       }
540       owner()->data()->sendAttributeUpdated(this);  // send updated if shape is changed
541     }
542     return aResult;
543   }
544
545   if (aContext->groupName() == ModelAPI_ResultConstruction::group()) {
546     Handle(TDataStd_Integer) anIndex;
547     if (aSelLab.FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
548       std::shared_ptr<Model_ResultConstruction> aConstructionContext =
549         std::dynamic_pointer_cast<Model_ResultConstruction>(aContext);
550       bool aModified = true;
551       bool aValid = aConstructionContext->update(anIndex->Get(), owner()->document(), aModified);
552       setInvalidIfFalse(aSelLab, aValid);
553       if (aModified)
554         owner()->data()->sendAttributeUpdated(this);
555       return aValid;
556     }
557   }
558   return setInvalidIfFalse(aSelLab, false); // unknown case
559 }
560
561 void Model_AttributeSelection::selectBody(
562   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape)
563 {
564   // perform the selection
565   TNaming_Selector aSel(selectionLabel());
566   TopoDS_Shape aContext;
567
568   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theContext);//myRef.value()
569   if (aBody) {
570     aContext = aBody->shape()->impl<TopoDS_Shape>();
571   } else {
572     ResultPtr aResult =
573       std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
574     if (aResult) {
575       aContext = aResult->shape()->impl<TopoDS_Shape>();
576     } else {
577       Events_InfoMessage("Model_AttributeSelection", "A result with shape is expected").send();
578       return;
579     }
580   }
581
582   // with "recover" feature the selected context may be not up to date (issue 1710)
583   Handle(TNaming_NamedShape) aResult;
584   TDF_Label aSelLab = selectionLabel();
585   TopoDS_Shape aNewContext = aContext;
586   bool isUpdated = true;
587   while(!aNewContext.IsNull() && isUpdated) {
588     // searching for the very last shape that was produced from this one
589     isUpdated = false;
590     if (!TNaming_Tool::HasLabel(aSelLab, aNewContext))
591       // to avoid crash of TNaming_SameShapeIterator if pure shape does not exists
592       break;
593     for(TNaming_SameShapeIterator anIter(aNewContext, aSelLab); anIter.More(); anIter.Next()) {
594       TDF_Label aNSLab = anIter.Label();
595       if (!scope().Contains(aNSLab))
596         continue;
597       Handle(TNaming_NamedShape) aNS;
598       if (aNSLab.FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
599         for(TNaming_Iterator aShapesIter(aNS); aShapesIter.More(); aShapesIter.Next()) {
600           if (aShapesIter.Evolution() == TNaming_SELECTED)
601             continue; // don't use the selection evolution
602           if (!aShapesIter.OldShape().IsNull() && aShapesIter.OldShape().IsSame(aNewContext)) {
603              // found the original shape
604             aNewContext = aShapesIter.NewShape(); // go to the newer shape
605             isUpdated = true;
606             break;
607           }
608         }
609       }
610     }
611   }
612   if (aNewContext.IsNull()) { // a context is already deleted
613     setInvalidIfFalse(aSelLab, false);
614     Events_InfoMessage("Model_AttributeSelection", "Failed to select shape already deleted").send();
615     return;
616   }
617
618   TopoDS_Shape aNewSub = theSubShape ? theSubShape->impl<TopoDS_Shape>() : aContext;
619   if (!aNewSub.IsEqual(aContext)) { // searching for subshape in the new context
620     bool isFound = false;
621     TopExp_Explorer anExp(aNewContext, aNewSub.ShapeType());
622     for(; anExp.More(); anExp.Next()) {
623       if (anExp.Current().IsEqual(aNewSub)) {
624         isFound = true;
625         break;
626       }
627     }
628     if (!isFound) { // sub-shape is not found in the up-to-date instance of the context shape
629       // if context is sub-result of compound/compsolid, selection of sub-shape better propagate to
630       // the main result (which is may be modified), case is in 1799
631       ResultCompSolidPtr aMain = ModelAPI_Tools::compSolidOwner(theContext);
632       if (aMain.get()) {
633         selectBody(aMain, theSubShape);
634         return;
635       }
636       setInvalidIfFalse(aSelLab, false);
637       Events_InfoMessage("Model_AttributeSelection",
638         "Failed to select sub-shape already modified").send();
639       return;
640     }
641   }
642
643   /// fix for issue 411: result modified shapes must not participate in this selection mechanism
644   if (!aContext.IsNull()) {
645     FeaturePtr aFeatureOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
646     bool aEraseResults = false;
647     if (aFeatureOwner.get()) {
648       aEraseResults = !aFeatureOwner->results().empty();
649       if (aEraseResults) // erase results without flash deleted and redisplay: do it after Select
650         aFeatureOwner->removeResults(0, false, false);
651     }
652     aSel.Select(aNewSub, aNewContext);
653
654     if (aEraseResults) { // flash after Select : in Groups it makes selection with shift working
655       static Events_Loop* aLoop = Events_Loop::loop();
656       static const Events_ID kDeletedEvent = aLoop->eventByName(EVENT_OBJECT_DELETED);
657       aLoop->flush(kDeletedEvent);
658     }
659   }
660 }
661
662 bool Model_AttributeSelection::selectPart(
663   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape,
664   const bool theUpdate)
665 {
666   ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(theContext);
667   if (!aPart.get() || !aPart->isActivated())
668     return true; // postponed naming
669   if (theUpdate) {
670     Handle(TDataStd_Integer) anIndex;
671     if (selectionLabel().FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
672       // by internal selection
673       if (anIndex->Get() > 0) {
674         // update the selection by index
675         return aPart->updateInPart(anIndex->Get());
676       } else {
677         return true; // nothing to do, referencing just by name
678       }
679     }
680     return true; // nothing to do, referencing just by name
681   }
682   // store the shape (in case part is not loaded it should be useful
683   TopoDS_Shape aShape;
684   std::string aName = theContext->data()->name();
685   if (!theSubShape.get() || theSubShape->isNull()) {// the whole part shape is selected
686     aShape = theContext->shape()->impl<TopoDS_Shape>();
687   } else {
688     aShape = theSubShape->impl<TopoDS_Shape>();
689     int anIndex;
690     aName += "/" + aPart->nameInPart(theSubShape, anIndex);
691     TDataStd_Integer::Set(selectionLabel(), anIndex);
692   }
693   TNaming_Builder aBuilder(selectionLabel());
694   aBuilder.Select(aShape, aShape);
695   // identify by name in the part
696   TDataStd_Name::Set(selectionLabel(), aName.c_str());
697   return !aName.empty();
698 }
699
700 TDF_Label Model_AttributeSelection::selectionLabel()
701 {
702   return myRef.myRef->Label().FindChild(1);
703 }
704
705 /// prefixes of the shape names with centers defined
706 static std::map<ModelAPI_AttributeSelection::CenterType, std::string> kCENTERS_PREFIX;
707
708 /// returns the map that contains all possible prefixes of the center-names
709 static std::map<ModelAPI_AttributeSelection::CenterType, std::string>& centersMap()
710 {
711   if (kCENTERS_PREFIX.empty()) { // fill map by initial values
712     kCENTERS_PREFIX[ModelAPI_AttributeSelection::CIRCLE_CENTER] = "__cc";
713     kCENTERS_PREFIX[ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS] = "__eff";
714     kCENTERS_PREFIX[ModelAPI_AttributeSelection::ELLIPSE_SECOND_FOCUS] = "__esf";
715   }
716   return kCENTERS_PREFIX;
717 }
718
719 std::string Model_AttributeSelection::namingName(const std::string& theDefaultName)
720 {
721   std::string aName("");
722   if(!this->isInitialized())
723     return !theDefaultName.empty() ? theDefaultName : aName;
724
725   CenterType aCenterType = NOT_CENTER;
726   std::shared_ptr<GeomAPI_Shape> aSubSh = internalValue(aCenterType);
727   ResultPtr aCont = context();
728
729   if (!aCont.get()) // in case of selection of removed result
730     return "";
731
732   Model_SelectionNaming aSelNaming(selectionLabel());
733   std::string aResult = aSelNaming.namingName(
734     aCont, aSubSh, theDefaultName, owner()->document() != aCont->document());
735   if (aCenterType != NOT_CENTER) {
736     aResult += centersMap()[aCenterType];
737   }
738   return aResult;
739 }
740
741 // returns the center type and modifies the shape name if this name is center-name
742 static ModelAPI_AttributeSelection::CenterType centerTypeByName(std::string& theShapeName)
743 {
744   std::map<ModelAPI_AttributeSelection::CenterType, std::string>::iterator aPrefixIter =
745     centersMap().begin();
746   for(; aPrefixIter != centersMap().end(); aPrefixIter++) {
747     std::size_t aFound = theShapeName.find(aPrefixIter->second);
748     if (aFound != std::string::npos &&
749         aFound == theShapeName.size() - aPrefixIter->second.size()) {
750       theShapeName = theShapeName.substr(0, aFound);
751       return aPrefixIter->first;
752     }
753   }
754   return ModelAPI_AttributeSelection::NOT_CENTER;
755 }
756
757 // type ::= COMP | COMS | SOLD | SHEL | FACE | WIRE | EDGE | VERT
758 void Model_AttributeSelection::selectSubShape(
759   const std::string& theType, const std::string& theSubShapeName)
760 {
761   if(theSubShapeName.empty() || theType.empty()) return;
762
763   std::string aSubShapeName = theSubShapeName;
764   CenterType aCenterType = theType[0] == 'v' || theType[0] == 'V' ? // only for vertex-type
765     centerTypeByName(aSubShapeName) : NOT_CENTER;
766   std::string aType = aCenterType == NOT_CENTER ? theType : "EDGE"; // search for edge now
767
768   // first iteration is selection by name without center prefix, second - in case of problem,
769   // try with initial name
770   for(int aUseCenter = 1; aUseCenter >= 0; aUseCenter--) {
771     if (aUseCenter == 0 && aCenterType != NOT_CENTER) {
772       aSubShapeName = theSubShapeName;
773       aCenterType = NOT_CENTER;
774       aType = theType;
775     } else if (aUseCenter != 1) continue;
776
777     // check this is Part-name: 2 delimiters in the name
778     std::size_t aPartEnd = aSubShapeName.find('/');
779     if (aPartEnd != std::string::npos && aPartEnd != aSubShapeName.rfind('/')) {
780       std::string aPartName = aSubShapeName.substr(0, aPartEnd);
781       ObjectPtr aFound = owner()->document()->objectByName(ModelAPI_ResultPart::group(), aPartName);
782       if (aFound.get()) { // found such part, so asking it for the name
783         ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aFound);
784         std::string aNameInPart = aSubShapeName.substr(aPartEnd + 1);
785         int anIndex;
786         std::shared_ptr<GeomAPI_Shape> aSelected = aPart->shapeInPart(aNameInPart, aType, anIndex);
787         if (aSelected.get()) {
788           if (aCenterType != NOT_CENTER) {
789             if (!aSelected->isEdge())
790               continue;
791             std::shared_ptr<GeomAPI_Edge> aSelectedEdge(new GeomAPI_Edge(aSelected));
792             setValueCenter(aPart, aSelectedEdge, aCenterType);
793           } else
794             setValue(aPart, aSelected);
795           TDataStd_Integer::Set(selectionLabel(), anIndex);
796           return;
797         }
798       }
799     }
800
801     Model_SelectionNaming aSelNaming(selectionLabel());
802     std::shared_ptr<Model_Document> aDoc =
803       std::dynamic_pointer_cast<Model_Document>(owner()->document());
804     std::shared_ptr<GeomAPI_Shape> aShapeToBeSelected;
805     ResultPtr aCont;
806     if (aSelNaming.selectSubShape(aType, aSubShapeName, aDoc, aShapeToBeSelected, aCont)) {
807       // try to find the last context to find the up to date shape
808       if (aCont->shape().get() && !aCont->shape()->isNull() &&
809         aCont->groupName() == ModelAPI_ResultBody::group() && aDoc == owner()->document()) {
810         const TopoDS_Shape aConShape = aCont->shape()->impl<TopoDS_Shape>();
811         if (!aConShape.IsNull()) {
812           Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aConShape, selectionLabel());
813           if (!aNS.IsNull()) {
814             aNS = TNaming_Tool::CurrentNamedShape(aNS);
815             if (!aNS.IsNull()) {
816               TDF_Label aLab = aNS->Label();
817               while(aLab.Depth() != 7 && aLab.Depth() > 5)
818                 aLab = aLab.Father();
819               ObjectPtr anObj = aDoc->objects()->object(aLab);
820               if (anObj.get()) {
821                 ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(anObj);
822                 if (aRes)
823                   aCont = aRes;
824               }
825             }
826           }
827         }
828       }
829       if (aCenterType != NOT_CENTER) {
830         if (!aShapeToBeSelected->isEdge())
831           continue;
832         std::shared_ptr<GeomAPI_Edge> aSelectedEdge(new GeomAPI_Edge(aShapeToBeSelected));
833         setValueCenter(aCont, aSelectedEdge, aCenterType);
834       } else
835         setValue(aCont, aShapeToBeSelected);
836       return;
837     }
838   }
839
840   TDF_Label aSelLab = selectionLabel();
841   setInvalidIfFalse(aSelLab, false);
842   reset();
843 }
844
845 int Model_AttributeSelection::Id()
846 {
847   int anID = 0;
848   std::shared_ptr<GeomAPI_Shape> aSelection = value();
849   std::shared_ptr<GeomAPI_Shape> aContext = context()->shape();
850   // support for compsolids:
851   if (context().get() && ModelAPI_Tools::compSolidOwner(context()).get())
852     aContext = ModelAPI_Tools::compSolidOwner(context())->shape();
853
854
855   TopoDS_Shape aMainShape = aContext->impl<TopoDS_Shape>();
856   const TopoDS_Shape& aSubShape = aSelection->impl<TopoDS_Shape>();
857   // searching for the latest main shape
858   if (aSelection && !aSelection->isNull() &&
859     aContext   && !aContext->isNull())
860   {
861     std::shared_ptr<Model_Document> aDoc =
862       std::dynamic_pointer_cast<Model_Document>(context()->document());
863     if (aDoc.get()) {
864       Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel());
865       if (!aNS.IsNull()) {
866         aMainShape = TNaming_Tool::CurrentShape(aNS);
867       }
868     }
869
870     TopTools_IndexedMapOfShape aSubShapesMap;
871     TopExp::MapShapes(aMainShape, aSubShapesMap);
872     anID = aSubShapesMap.FindIndex(aSubShape);
873   }
874   return anID;
875 }
876
877 void Model_AttributeSelection::setId(int theID)
878 {
879   const ResultPtr& aContext = context();
880   std::shared_ptr<GeomAPI_Shape> aSelection;
881
882   std::shared_ptr<GeomAPI_Shape> aContextShape = aContext->shape();
883   // support for compsolids:
884   if (aContext.get() && ModelAPI_Tools::compSolidOwner(aContext).get())
885     aContextShape = ModelAPI_Tools::compSolidOwner(aContext)->shape();
886
887   TopoDS_Shape aMainShape = aContextShape->impl<TopoDS_Shape>();
888   // searching for the latest main shape
889   if (theID > 0 &&
890       aContextShape && !aContextShape->isNull())
891   {
892     std::shared_ptr<Model_Document> aDoc =
893       std::dynamic_pointer_cast<Model_Document>(aContext->document());
894     if (aDoc.get()) {
895       Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel());
896       if (!aNS.IsNull()) {
897         aMainShape = TNaming_Tool::CurrentShape(aNS);
898       }
899     }
900
901     TopTools_IndexedMapOfShape aSubShapesMap;
902     TopExp::MapShapes(aMainShape, aSubShapesMap);
903     const TopoDS_Shape& aSelShape = aSubShapesMap.FindKey(theID);
904
905     std::shared_ptr<GeomAPI_Shape> aResult(new GeomAPI_Shape);
906     aResult->setImpl(new TopoDS_Shape(aSelShape));
907
908     aSelection = aResult;
909   }
910
911   setValue(aContext, aSelection);
912 }
913
914 std::string Model_AttributeSelection::contextName(const ResultPtr& theContext) const
915 {
916   std::string aResult;
917   if (owner()->document() != theContext->document()) {
918     if (theContext->document() == ModelAPI_Session::get()->moduleDocument()) {
919       aResult = theContext->document()->kind() + "/";
920     } else {
921       ResultPtr aDocRes = ModelAPI_Tools::findPartResult(
922         ModelAPI_Session::get()->moduleDocument(), theContext->document());
923       if (aDocRes.get()) {
924         aResult = aDocRes->data()->name() + "/";
925       }
926     }
927   }
928   aResult += theContext->data()->name();
929   return aResult;
930 }
931
932 void Model_AttributeSelection::updateInHistory()
933 {
934   ResultPtr aContext = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
935   // only bodies may be modified later in the history, don't do anything otherwise
936   if (!aContext.get() || aContext->groupName() != ModelAPI_ResultBody::group())
937     return;
938   std::shared_ptr<Model_Data> aContData = std::dynamic_pointer_cast<Model_Data>(aContext->data());
939   if (!aContData.get() || !aContData->isValid())
940     return;
941   TDF_Label aContLab = aContData->label(); // named shape where the selected context is located
942   Handle(TNaming_NamedShape) aContNS;
943   if (!aContLab.FindAttribute(TNaming_NamedShape::GetID(), aContNS))
944     return;
945   std::shared_ptr<Model_Document> aDoc =
946     std::dynamic_pointer_cast<Model_Document>(aContext->document());
947   FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
948   FeaturePtr aCurrentModifierFeat = aDoc->feature(aContext);
949   // iterate the context shape modifications in order to find a feature that is upper in history
950   // that this one and is really modifies the referenced result to refer to it
951   ResultPtr aModifierResFound;
952   TNaming_Iterator aPairIter(aContNS);
953   if (!aPairIter.More())
954     return;
955   TopoDS_Shape aNewShape = aPairIter.NewShape();
956   bool anIterate = true;
957   // trying to update also the sub-shape selected
958   GeomShapePtr aSubShape = value();
959   if (aSubShape.get() && aSubShape->isEqual(aContext->shape()))
960     aSubShape.reset();
961
962   while(anIterate) {
963     anIterate = false;
964     TNaming_SameShapeIterator aModifIter(aNewShape, aContLab);
965     for(; aModifIter.More(); aModifIter.Next()) {
966       ResultPtr aModifierObj = std::dynamic_pointer_cast<ModelAPI_Result>
967         (aDoc->objects()->object(aModifIter.Label().Father()));
968       if (!aModifierObj.get())
969         break;
970       FeaturePtr aModifierFeat = aDoc->feature(aModifierObj);
971       if (!aModifierFeat.get())
972         break;
973       if (aModifierFeat == aThisFeature || aDoc->objects()->isLater(aModifierFeat, aThisFeature))
974         continue; // the modifier feature is later than this, so, should not be used
975       if (aCurrentModifierFeat == aModifierFeat ||
976         aDoc->objects()->isLater(aCurrentModifierFeat, aModifierFeat))
977         continue; // the current modifier is later than the found, so, useless
978       Handle(TNaming_NamedShape) aNewNS;
979       aModifIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNewNS);
980       if (aNewNS->Evolution() == TNaming_MODIFY || aNewNS->Evolution() == TNaming_GENERATED) {
981         aModifierResFound = aModifierObj;
982         aCurrentModifierFeat = aModifierFeat;
983         TNaming_Iterator aPairIter(aNewNS);
984         aNewShape = aPairIter.NewShape();
985         anIterate = true;
986         break;
987       } else if (aNewNS->Evolution() == TNaming_DELETE) { // a shape was deleted => result is null
988         ResultPtr anEmptyContext;
989         std::shared_ptr<GeomAPI_Shape> anEmptyShape;
990         setValue(anEmptyContext, anEmptyShape); // nullify the selection
991         return;
992       } else { // not-precessed modification => don't support it
993         continue;
994       }
995     }
996   }
997   if (aModifierResFound.get()) {
998     // update scope to reset to a new one
999     myScope.Clear();
1000     myRef.setValue(aModifierResFound);
1001     // if context shape type is changed to more complicated and this context is selected, split
1002     if (myParent &&!aSubShape.get() && aModifierResFound->shape().get() && aContext->shape().get())
1003     {
1004       TopoDS_Shape anOldShape = aContext->shape()->impl<TopoDS_Shape>();
1005       TopoDS_Shape aNewShape = aModifierResFound->shape()->impl<TopoDS_Shape>();
1006       if (!anOldShape.IsNull() && !aNewShape.IsNull() &&
1007         anOldShape.ShapeType() != aNewShape.ShapeType() &&
1008         (aNewShape.ShapeType() == TopAbs_COMPOUND || aNewShape.ShapeType() == TopAbs_COMPSOLID)) {
1009         // prepare for split in "update"
1010         TDF_Label aSelLab = selectionLabel();
1011         split(aContext, aNewShape, anOldShape.ShapeType());
1012       }
1013     }
1014     update(); // it must recompute a new sub-shape automatically
1015   }
1016 }
1017
1018 void Model_AttributeSelection::setParent(Model_AttributeSelectionList* theParent)
1019 {
1020   myParent = theParent;
1021 }