Salome HOME
Issue #971: Update OB on duplicate part: send event on creation of a new document...
[modules/shaper.git] / src / Model / Model_AttributeSelection.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D
2
3 // File:        Model_AttributeSelection.cpp
4 // Created:     2 Oct 2014
5 // Author:      Mikhail PONIKAROV
6
7 #include "Model_AttributeSelection.h"
8 #include "Model_Application.h"
9 #include "Model_Events.h"
10 #include "Model_Data.h"
11 #include "Model_Document.h"
12 #include "Model_SelectionNaming.h"
13 #include <ModelAPI_Feature.h>
14 #include <ModelAPI_ResultBody.h>
15 #include <ModelAPI_ResultConstruction.h>
16 #include <ModelAPI_ResultPart.h>
17 #include <ModelAPI_CompositeFeature.h>
18 #include <ModelAPI_Tools.h>
19 #include <GeomAPI_Shape.h>
20 #include <GeomAPI_PlanarEdges.h>
21 #include <Events_Error.h>
22
23 #include <TNaming_Selector.hxx>
24 #include <TNaming_NamedShape.hxx>
25 #include <TNaming_Tool.hxx>
26 #include <TNaming_Builder.hxx>
27 #include <TNaming_Localizer.hxx>
28 #include <TopoDS_Shape.hxx>
29 #include <TopoDS_Compound.hxx>
30 #include <TDataStd_IntPackedMap.hxx>
31 #include <TDataStd_Integer.hxx>
32 #include <TDataStd_UAttribute.hxx>
33 #include <TDataStd_Name.hxx>
34 #include <TopTools_MapOfShape.hxx>
35 #include <TopTools_IndexedMapOfShape.hxx>
36 #include <TopTools_MapIteratorOfMapOfShape.hxx>
37 #include <TopTools_ListOfShape.hxx>
38 #include <NCollection_DataMap.hxx>
39 #include <TopExp_Explorer.hxx>
40 #include <TDF_LabelMap.hxx>
41 #include <BRep_Tool.hxx>
42 #include <BRep_Builder.hxx>
43 #include <TopoDS_Edge.hxx>
44 #include <TopoDS.hxx>
45 #include <TopExp.hxx>
46 #include <TColStd_MapOfTransient.hxx>
47 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
48 #include <TopTools_ListIteratorOfListOfShape.hxx>
49 #include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
50 #include <gp_Pnt.hxx>
51 #include <Precision.hxx>
52 #include <TDF_ChildIterator.hxx>
53 #include <TDF_ChildIDIterator.hxx>
54 #include <TDataStd_Name.hxx>
55 #include <TopAbs_ShapeEnum.hxx>
56 #include <TopoDS_Iterator.hxx>
57 #include <TNaming_Iterator.hxx>
58 #include <BRep_Builder.hxx>
59 using namespace std;
60 //#define DEB_NAMING 1
61 #ifdef DEB_NAMING
62 #include <BRepTools.hxx>
63 #endif
64 /// adeed to the index in the packed map to signalize that the vertex of edge is seleted
65 /// (multiplied by the index of the edge)
66 static const int kSTART_VERTEX_DELTA = 1000000;
67 // identifier that there is simple reference: selection equals to context
68 Standard_GUID kSIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb29");
69 // simple reference in the construction
70 Standard_GUID kCONSTUCTION_SIMPLE_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb28");
71 // reference to Part sub-object
72 Standard_GUID kPART_REF_ID("635eacb2-a1d6-4dec-8348-471fae17cb27");
73 // selection is invalid after recomputation
74 Standard_GUID kINVALID_SELECTION("bce47fd7-80fa-4462-9d63-2f58acddd49d");
75
76 // on this label is stored:
77 // TNaming_NamedShape - selected shape
78 // TNaming_Naming - topological selection information (for the body)
79 // TDataStd_IntPackedMap - indexes of edges in composite element (for construction)
80 // TDataStd_Integer - type of the selected shape (for construction)
81 // TDF_Reference - from ReferenceAttribute, the context
82 void Model_AttributeSelection::setValue(const ResultPtr& theContext,
83   const std::shared_ptr<GeomAPI_Shape>& theSubShape, const bool theTemporarily)
84 {
85   if (theTemporarily) { // just keep the stored without DF update
86     myTmpContext = theContext;
87     myTmpSubShape = theSubShape;
88     owner()->data()->sendAttributeUpdated(this);
89     return;
90   } else {
91     myTmpContext.reset();
92     myTmpSubShape.reset();
93   }
94
95   const std::shared_ptr<GeomAPI_Shape>& anOldShape = value();
96   bool isOldContext = theContext == myRef.value();
97   bool isOldShape = isOldContext &&
98     (theSubShape == anOldShape || (theSubShape && anOldShape && theSubShape->isEqual(anOldShape)));
99   if (isOldShape) return; // shape is the same, so context is also unchanged
100   // update the referenced object if needed
101   if (!isOldContext)
102     myRef.setValue(theContext);
103
104   // do noth use naming if selected shape is result shape itself, but not sub-shape
105   TDF_Label aSelLab = selectionLabel();
106   aSelLab.ForgetAttribute(kSIMPLE_REF_ID);
107   aSelLab.ForgetAttribute(kCONSTUCTION_SIMPLE_REF_ID);
108   aSelLab.ForgetAttribute(kINVALID_SELECTION);
109
110   bool isDegeneratedEdge = false;
111   // do not use the degenerated edge as a shape, a null context and shape is used in the case
112   if (theSubShape.get() && !theSubShape->isNull() && theSubShape->isEdge()) {
113     const TopoDS_Shape& aSubShape = theSubShape->impl<TopoDS_Shape>();
114     if (aSubShape.ShapeType() == TopAbs_EDGE)
115       isDegeneratedEdge = BRep_Tool::Degenerated(TopoDS::Edge(aSubShape)) == Standard_True;
116   }
117   if (!theContext.get() || isDegeneratedEdge) {
118     // to keep the reference attribute label
119     TDF_Label aRefLab = myRef.myRef->Label();
120     aSelLab.ForgetAllAttributes(true);
121     myRef.myRef = TDF_Reference::Set(aSelLab.Father(), aSelLab.Father());
122     return;
123   }
124   if (theContext->groupName() == ModelAPI_ResultBody::group()) {
125     // do not select the whole shape for body:it is already must be in the data framework
126     // equal and null selected objects mean the same: object is equal to context,
127     if (theContext->shape().get() && 
128         (theContext->shape()->isEqual(theSubShape) || !theSubShape.get())) {
129       aSelLab.ForgetAllAttributes(true);
130       TDataStd_UAttribute::Set(aSelLab, kSIMPLE_REF_ID);
131     } else {
132       selectBody(theContext, theSubShape);
133     }
134   } else if (theContext->groupName() == ModelAPI_ResultConstruction::group()) {
135     if (!theSubShape.get()) {
136       // to sub, so the whole result is selected
137       aSelLab.ForgetAllAttributes(true);
138       TDataStd_UAttribute::Set(aSelLab, kCONSTUCTION_SIMPLE_REF_ID);
139       ResultConstructionPtr aConstruction = 
140         std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theContext);
141       if (aConstruction->isInfinite()) {
142         // For correct naming selection, put the shape into the naming structure.
143         // It seems sub-shapes are not needed: only this shape is (and can be ) selected.
144         TNaming_Builder aBuilder(aSelLab);
145         aBuilder.Generated(theContext->shape()->impl<TopoDS_Shape>());
146         std::shared_ptr<Model_Document> aMyDoc = 
147           std::dynamic_pointer_cast<Model_Document>(owner()->document());
148         std::string aName = theContext->data()->name();
149         aMyDoc->addNamingName(aSelLab, aName);
150         TDataStd_Name::Set(aSelLab, aName.c_str());
151       } else {  // for sketch the naming is needed in DS
152         BRep_Builder aCompoundBuilder;
153         TopoDS_Compound aComp;
154         aCompoundBuilder.MakeCompound(aComp);
155         for(int a = 0; a < aConstruction->facesNum(); a++) {
156           TopoDS_Shape aFace = aConstruction->face(a)->impl<TopoDS_Shape>();
157           aCompoundBuilder.Add(aComp, aFace);
158         }
159         std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape);
160         aShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aComp));
161         selectConstruction(theContext, aShape);
162       }
163     } else {
164       selectConstruction(theContext, theSubShape);
165     }
166   } else if (theContext->groupName() == ModelAPI_ResultPart::group()) {
167     aSelLab.ForgetAllAttributes(true);
168     TDataStd_UAttribute::Set(aSelLab, kPART_REF_ID);
169     selectPart(theContext, theSubShape);
170   }
171   //the attribute initialized state should be changed by sendAttributeUpdated only
172   //myIsInitialized = true;
173
174   owner()->data()->sendAttributeUpdated(this);
175
176   std::string aSelName = namingName();
177   if(!aSelName.empty())
178     TDataStd_Name::Set(selectionLabel(), aSelName.c_str()); //set name
179 }
180
181 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
182 {
183   if (myTmpContext.get() || myTmpSubShape.get()) {
184     return myTmpSubShape;
185   }
186
187   std::shared_ptr<GeomAPI_Shape> aResult;
188   TDF_Label aSelLab = selectionLabel();
189   if (aSelLab.IsAttribute(kINVALID_SELECTION))
190     return aResult;
191
192   if (myRef.isInitialized()) {
193     if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
194       ResultPtr aContext = context();
195       if (!aContext.get()) 
196         return aResult; // empty result
197       return aContext->shape();
198     }
199     if (aSelLab.IsAttribute(kCONSTUCTION_SIMPLE_REF_ID)) { // it is just reference to construction, nothing is in value
200         return aResult; // empty result
201     }
202     if (aSelLab.IsAttribute(kPART_REF_ID)) {
203       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(context());
204       if (!aPart.get() || !aPart->isActivated())
205         return std::shared_ptr<GeomAPI_Shape>(); // postponed naming needed
206       Handle(TDataStd_Integer) anIndex;
207       if (selectionLabel().FindAttribute(TDataStd_Integer::GetID(), anIndex)) {
208         if (anIndex->Get()) { // special selection attribute was created, use it
209           return aPart->selectionValue(anIndex->Get());
210         } else { // face with name is already in the data model, so try to take it by name
211           Handle(TDataStd_Name) aName;
212           if (selectionLabel().FindAttribute(TDataStd_Name::GetID(), aName)) {
213             std::string aSubShapeName(TCollection_AsciiString(aName->Get()).ToCString());
214             std::size_t aPartEnd = aSubShapeName.find('/');
215             if (aPartEnd != string::npos && aPartEnd != aSubShapeName.rfind('/')) {
216               string aNameInPart = aSubShapeName.substr(aPartEnd + 1);
217               int anIndex;
218               std::string aType; // to reuse already existing selection the type is not needed
219               return aPart->shapeInPart(aNameInPart, aType, anIndex);
220             }
221           }
222         }
223       }
224     }
225
226     Handle(TNaming_NamedShape) aSelection;
227     if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
228       TopoDS_Shape aSelShape = aSelection->Get();
229       aResult = std::shared_ptr<GeomAPI_Shape>(new GeomAPI_Shape);
230       aResult->setImpl(new TopoDS_Shape(aSelShape));
231     } else { // for simple construction element: just shape of this construction element
232       ResultConstructionPtr aConstr = 
233         std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(context());
234       if (aConstr) {
235         return aConstr->shape();
236       }
237     }
238   }
239   return aResult;
240 }
241
242 bool Model_AttributeSelection::isInvalid()
243 {
244   return selectionLabel().IsAttribute(kINVALID_SELECTION) == Standard_True;
245 }
246
247 bool Model_AttributeSelection::isInitialized()
248 {
249   if (ModelAPI_AttributeSelection::isInitialized()) { // additional checkings if it is initialized
250     std::shared_ptr<GeomAPI_Shape> aResult;
251     if (myRef.isInitialized()) {
252       TDF_Label aSelLab = selectionLabel();
253       if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
254         ResultPtr aContext = context();
255         return aContext.get() != NULL;
256       }
257       if (aSelLab.IsAttribute(kCONSTUCTION_SIMPLE_REF_ID)) { // it is just reference to construction, nothing is in value
258           return true;
259       }
260
261       Handle(TNaming_NamedShape) aSelection;
262       if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
263         return !aSelection->Get().IsNull();
264       } else { // for simple construction element: just shape of this construction element
265         ResultConstructionPtr aConstr = 
266           std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(context());
267         if (aConstr.get()) {
268           return aConstr->shape().get() != NULL;
269         }
270       }
271     }
272   }
273   return false;
274 }
275
276 Model_AttributeSelection::Model_AttributeSelection(TDF_Label& theLabel)
277   : myRef(theLabel)
278 {
279   myIsInitialized = myRef.isInitialized();
280 }
281
282 void Model_AttributeSelection::setID(const std::string theID)
283 {
284   myRef.setID(theID);
285   ModelAPI_AttributeSelection::setID(theID);
286 }
287
288 ResultPtr Model_AttributeSelection::context() {
289   if (myTmpContext.get() || myTmpSubShape.get()) {
290     return myTmpContext;
291   }
292
293   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
294   // for parts there could be same-data result, so take the last enabled
295   if (aResult.get() && aResult->groupName() == ModelAPI_ResultPart::group()) {
296     int aSize = aResult->document()->size(ModelAPI_ResultPart::group());
297     for(int a = aSize - 1; a >= 0; a--) {
298       ObjectPtr aPart = aResult->document()->object(ModelAPI_ResultPart::group(), a);
299       if (aPart.get() && aPart->data() == aResult->data()) {
300         ResultPtr aPartResult = std::dynamic_pointer_cast<ModelAPI_Result>(aPart);
301         FeaturePtr anOwnerFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
302         // check that this result is not this-feature result (it is forbidden t oselect itself)
303         if (anOwnerFeature.get() && anOwnerFeature->firstResult() != aPartResult) {
304           return aPartResult;
305         }
306       }
307     }
308   }
309   return aResult;
310 }
311
312
313 void Model_AttributeSelection::setObject(const std::shared_ptr<ModelAPI_Object>& theObject)
314 {
315   ModelAPI_AttributeSelection::setObject(theObject);
316   myRef.setObject(theObject);
317 }
318
319 TDF_LabelMap& Model_AttributeSelection::scope()
320 {
321   if (myScope.IsEmpty()) { // create a new scope if not yet done
322     // gets all featueres with named shapes that are bofore this feature label (before in history)
323     DocumentPtr aMyDoc = owner()->document();
324     std::list<std::shared_ptr<ModelAPI_Feature> > allFeatures = aMyDoc->allFeatures();
325     std::list<std::shared_ptr<ModelAPI_Feature> >::iterator aFIter = allFeatures.begin();
326     bool aMePassed = false;
327     CompositeFeaturePtr aComposite = 
328       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(owner());
329     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
330     CompositeFeaturePtr aCompositeOwner, aCompositeOwnerOwner;
331     if (aFeature.get()) {
332       aCompositeOwner = ModelAPI_Tools::compositeOwner(aFeature);
333       if (aCompositeOwner.get()) {
334          aCompositeOwnerOwner = ModelAPI_Tools::compositeOwner(aCompositeOwner);
335       }
336     }
337     // for group Scope is not limitet: this is always up to date objects
338     bool isGroup = aFeature.get() && aFeature->getKind() == "Group";
339     for(; aFIter != allFeatures.end(); aFIter++) {
340       if (*aFIter == owner()) {  // the left features are created later (except subs of composite)
341         aMePassed = true;
342         continue;
343       }
344       if (isGroup) aMePassed = false;
345       bool isInScope = !aMePassed;
346       if (!isInScope && aComposite.get()) { // try to add sub-elements of composite if this is composite
347         if (aComposite->isSub(*aFIter))
348           isInScope = true;
349       }
350       // remove the composite-owner of this feature (sketch in extrusion-cut)
351       if (isInScope && (aCompositeOwner == *aFIter || aCompositeOwnerOwner == *aFIter))
352         isInScope = false;
353
354       if (isInScope && aFIter->get() && (*aFIter)->data()->isValid()) {
355         TDF_Label aFeatureLab = std::dynamic_pointer_cast<Model_Data>(
356           (*aFIter)->data())->label().Father();
357         TDF_ChildIDIterator aNSIter(aFeatureLab, TNaming_NamedShape::GetID(), 1);
358         for(; aNSIter.More(); aNSIter.Next()) {
359           Handle(TNaming_NamedShape) aNS = Handle(TNaming_NamedShape)::DownCast(aNSIter.Value());
360           if (!aNS.IsNull() && aNS->Evolution() != TNaming_SELECTED) {
361             myScope.Add(aNS->Label());
362           }
363         }
364       }
365     }
366   }
367   return myScope;
368 }
369
370 /// produces theEdge orientation relatively to theContext face
371 int edgeOrientation(const TopoDS_Shape& theContext, TopoDS_Edge& theEdge)
372 {
373   if (theContext.ShapeType() != TopAbs_FACE)
374     return 0;
375   TopoDS_Face aContext = TopoDS::Face(theContext);
376   if (theEdge.Orientation() == TopAbs_FORWARD) 
377     return 1;
378   if (theEdge.Orientation() == TopAbs_REVERSED) 
379     return -1;
380   return 0; // unknown
381 }
382
383 /// Sets the invalid flag if flag is false, or removes it if "true"
384 /// Returns theFlag
385 static bool setInvalidIfFalse(TDF_Label& theLab, const bool theFlag) {
386   if (theFlag) {
387     theLab.ForgetAttribute(kINVALID_SELECTION);
388   } else {
389     TDataStd_UAttribute::Set(theLab, kINVALID_SELECTION);
390   }
391   return theFlag;
392 }
393
394 bool Model_AttributeSelection::update()
395 {
396   TDF_Label aSelLab = selectionLabel();
397   ResultPtr aContext = context();
398   if (!aContext.get()) 
399     return setInvalidIfFalse(aSelLab, false);
400   if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // it is just reference to shape, not sub-shape
401     return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull());
402   }
403   if (aSelLab.IsAttribute(kCONSTUCTION_SIMPLE_REF_ID)) { // it is just reference to construction, not sub-shape
404     // if there is a sketch, the sketch-naming must be updated
405     ResultConstructionPtr aConstruction = 
406       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
407     if (!aConstruction->isInfinite()) {
408       BRep_Builder aCompoundBuilder;
409       TopoDS_Compound aComp;
410       aCompoundBuilder.MakeCompound(aComp);
411       for(int a = 0; a < aConstruction->facesNum(); a++) {
412         TopoDS_Shape aFace = aConstruction->face(a)->impl<TopoDS_Shape>();
413         aCompoundBuilder.Add(aComp, aFace);
414       }
415       std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape);
416       aShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aComp));
417       selectConstruction(aContext, aShape);
418     }
419     return setInvalidIfFalse(aSelLab, aContext->shape() && !aContext->shape()->isNull());
420   }
421
422   if (aSelLab.IsAttribute(kPART_REF_ID)) { // it is reference to the part object
423     std::shared_ptr<GeomAPI_Shape> aNoSelection;
424     return setInvalidIfFalse(aSelLab, selectPart(aContext, aNoSelection, true));
425   }
426
427   if (aContext->groupName() == ModelAPI_ResultBody::group()) {
428     // body: just a named shape, use selection mechanism from OCCT
429     TNaming_Selector aSelector(aSelLab);
430     bool aResult = aSelector.Solve(scope()) == Standard_True;
431     owner()->data()->sendAttributeUpdated(this);
432     return setInvalidIfFalse(aSelLab, aResult);
433   } else if (aContext->groupName() == ModelAPI_ResultConstruction::group()) {
434     // construction: identification by the results indexes, recompute faces and
435     // take the face that more close by the indexes
436     ResultConstructionPtr aConstructionContext = 
437       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
438     FeaturePtr aContextFeature = aContext->document()->feature(aContext);
439     // sketch sub-element
440     if (aConstructionContext && 
441         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aContextFeature).get())
442     {
443       TDF_Label aLab = myRef.myRef->Label();
444       // getting a type of selected shape
445       Handle(TDataStd_Integer) aTypeAttr;
446       if (!aLab.FindAttribute(TDataStd_Integer::GetID(), aTypeAttr)) {
447         return setInvalidIfFalse(aSelLab, false);
448       }
449       TopAbs_ShapeEnum aShapeType = (TopAbs_ShapeEnum)(aTypeAttr->Get());
450       // selected indexes will be needed in each "if"
451       Handle(TDataStd_IntPackedMap) aSubIds;
452       std::shared_ptr<GeomAPI_Shape> aNewSelected;
453       bool aNoIndexes = 
454         !aLab.FindAttribute(TDataStd_IntPackedMap::GetID(), aSubIds) || aSubIds->Extent() == 0;
455       // for now working only with composite features
456       CompositeFeaturePtr aComposite = 
457         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aContextFeature);
458       if (!aComposite.get() || aComposite->numberOfSubs() == 0) {
459         return setInvalidIfFalse(aSelLab, false);
460       }
461
462       if (aShapeType == TopAbs_FACE) { // compound is for the whole sketch selection
463         // If this is a wire with plane defined thin it is a sketch-like object
464         if (!aConstructionContext->facesNum()) // no faces, update can not work correctly
465           return setInvalidIfFalse(aSelLab, false);
466         // if there is no edges indexes, any face can be used: take the first
467         std::shared_ptr<GeomAPI_Shape> aNewSelected;
468         if (aNoIndexes) {
469           aNewSelected = aConstructionContext->face(0);
470         } else { // searching for most looks-like initial face by the indexes
471           // prepare edges of the current resut for the fast searching
472           NCollection_DataMap<Handle(Geom_Curve), int> allCurves; // curves and orientations of edges
473           const int aSubNum = aComposite->numberOfSubs();
474           for(int a = 0; a < aSubNum; a++) {
475             int aSubID = aComposite->subFeatureId(a);
476             if (aSubIds->Contains(aSubID)) {
477               FeaturePtr aSub = aComposite->subFeature(a);
478               const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aSub->results();
479               std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes;
480               for(aRes = aResults.cbegin(); aRes != aResults.cend(); aRes++) {
481                 ResultConstructionPtr aConstr = 
482                   std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aRes);
483                 if (aConstr->shape() && aConstr->shape()->isEdge()) {
484                   const TopoDS_Shape& aResShape = aConstr->shape()->impl<TopoDS_Shape>();
485                   TopoDS_Edge anEdge = TopoDS::Edge(aResShape);
486                   if (!anEdge.IsNull()) {
487                     Standard_Real aFirst, aLast;
488                     Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
489                     // searching for orientation information
490                     int anOrient = 0;
491                     Handle(TDataStd_Integer) anInt;
492                     if (aSelLab.FindChild(aSubID).FindAttribute(TDataStd_Integer::GetID(), anInt)){
493                       anOrient = anInt->Get();
494                     }
495                     allCurves.Bind(aCurve, anOrient);
496                   }
497                 }
498               }
499             }
500           }
501           int aBestFound = 0; // best number of found edges (not percentage: issue 1019)
502           int aBestOrient = 0; // for the equal "BestFound" additional parameter is orientation
503           for(int aFaceIndex = 0; aFaceIndex < aConstructionContext->facesNum(); aFaceIndex++) {
504             int aFound = 0, aNotFound = 0, aSameOrientation = 0;
505             TopoDS_Face aFace = 
506               TopoDS::Face(aConstructionContext->face(aFaceIndex)->impl<TopoDS_Shape>());
507             TopExp_Explorer anEdgesExp(aFace, TopAbs_EDGE);
508             TColStd_MapOfTransient alreadyProcessed; // to avoid counting edges with same curved (841)
509             for(; anEdgesExp.More(); anEdgesExp.Next()) {
510               TopoDS_Edge anEdge = TopoDS::Edge(anEdgesExp.Current());
511               if (!anEdge.IsNull()) {
512                 Standard_Real aFirst, aLast;
513                 Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
514                 if (alreadyProcessed.Contains(aCurve))
515                   continue;
516                 alreadyProcessed.Add(aCurve);
517                 if (allCurves.IsBound(aCurve)) {
518                   aFound++;
519                   int anOrient = allCurves.Find(aCurve);
520                   if (anOrient != 0) {  // extra comparision score is orientation
521                     if (edgeOrientation(aFace, anEdge) == anOrient)
522                       aSameOrientation++;
523                   }
524                 } else {
525                   aNotFound++;
526                 }
527               }
528             }
529             if (aFound + aNotFound != 0) {
530               if (aFound > aBestFound || 
531                   (aFound == aBestFound && aSameOrientation > aBestOrient)) {
532                 aBestFound = aFound;
533                 aBestOrient = aSameOrientation;
534                 aNewSelected = aConstructionContext->face(aFaceIndex);
535               }
536             }
537           }
538         }
539         if (aNewSelected) { // store this new selection
540           selectConstruction(aContext, aNewSelected);
541           owner()->data()->sendAttributeUpdated(this);
542           return setInvalidIfFalse(aSelLab, true);
543         } else { // if the selection is not found, put the empty shape: it's better to have disappeared shape, than the old, the lost one
544           TNaming_Builder anEmptyBuilder(selectionLabel());
545           return setInvalidIfFalse(aSelLab, false);
546         }
547       } else if (aShapeType == TopAbs_EDGE) {
548         // just reselect the edge by the id
549         const int aSubNum = aComposite->numberOfSubs();
550         for(int a = 0; a < aSubNum; a++) {
551           // if aSubIds take any, the first appropriate
552           if (aSubIds->IsEmpty() || aSubIds->Contains(aComposite->subFeatureId(a))) {
553             // found the appropriate feature
554             FeaturePtr aFeature = aComposite->subFeature(a);
555             std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aResIter =
556               aFeature->results().cbegin();
557             for(;aResIter != aFeature->results().cend(); aResIter++) {
558               ResultConstructionPtr aRes = 
559                 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aResIter);
560               if (aRes && aRes->shape() && aRes->shape()->isEdge()) { // found!
561                 selectConstruction(aContext, aRes->shape());
562                 owner()->data()->sendAttributeUpdated(this);
563                 return setInvalidIfFalse(aSelLab, true);
564               }
565             }
566           }
567         }
568       } else if (aShapeType == TopAbs_VERTEX) {
569         // just reselect the vertex by the id of edge
570         const int aSubNum = aComposite->numberOfSubs();
571         for(int a = 0; a < aSubNum; a++) {
572           // if aSubIds take any, the first appropriate
573           int aFeatureID = aComposite->subFeatureId(a);
574           if (aSubIds->IsEmpty() || aSubIds->Contains(aFeatureID) ||
575             aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA) ||
576             aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA * 2)) {
577               // searching for deltas
578               int aVertexNum = 0;
579               if (aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA)) aVertexNum = 1;
580               else if (aSubIds->Contains(aFeatureID + kSTART_VERTEX_DELTA * 2)) aVertexNum = 2;
581               // found the feature with appropriate edge
582               FeaturePtr aFeature = aComposite->subFeature(a);
583               std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aResIter =
584                 aFeature->results().cbegin();
585               for(;aResIter != aFeature->results().cend(); aResIter++) {
586                 ResultConstructionPtr aRes = 
587                   std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aResIter);
588                 if (aRes && aRes->shape()) {
589                   if (aRes->shape()->isVertex() && aVertexNum == 0) { // found!
590                     selectConstruction(aContext, aRes->shape());
591                     owner()->data()->sendAttributeUpdated(this);
592                     return setInvalidIfFalse(aSelLab, true);
593                   } else if (aRes->shape()->isEdge() && aVertexNum > 0) {
594                     const TopoDS_Shape& anEdge = aRes->shape()->impl<TopoDS_Shape>();
595                     int aVIndex = 1;
596                     for(TopExp_Explorer aVExp(anEdge, TopAbs_VERTEX); aVExp.More(); aVExp.Next()) {
597                       if (aVIndex == aVertexNum) { // found!
598                         std::shared_ptr<GeomAPI_Shape> aVertex(new GeomAPI_Shape);
599                         aVertex->setImpl(new TopoDS_Shape(aVExp.Current()));
600                         selectConstruction(aContext, aVertex);
601                         owner()->data()->sendAttributeUpdated(this);
602                         return setInvalidIfFalse(aSelLab, true);
603                       }
604                       aVIndex++;
605                     }
606                   }
607                 }
608               }
609           }
610         }
611       }
612     } else { // simple construction element: the selected is that needed
613       selectConstruction(aContext, aContext->shape());
614       owner()->data()->sendAttributeUpdated(this);
615       return setInvalidIfFalse(aSelLab, true);
616     }
617   }
618   return setInvalidIfFalse(aSelLab, false); // unknown case
619 }
620
621
622 void Model_AttributeSelection::selectBody(
623   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape)
624 {
625   // perform the selection
626   TNaming_Selector aSel(selectionLabel());
627   TopoDS_Shape aContext;
628
629   ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(myRef.value());
630   if (aBody) {
631     aContext = aBody->shape()->impl<TopoDS_Shape>();
632   } else {
633     ResultPtr aResult = 
634       std::dynamic_pointer_cast<ModelAPI_Result>(myRef.value());
635     if (aResult) {
636       aContext = aResult->shape()->impl<TopoDS_Shape>();
637     } else {
638       Events_Error::send("A result with shape is expected");
639       return;
640     }
641   }
642   TopoDS_Shape aNewShape = theSubShape ? theSubShape->impl<TopoDS_Shape>() : aContext;
643   /// fix for issue 411: result modified shapes must not participate in this selection mechanism
644   FeaturePtr aFeatureOwner = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
645   if (aFeatureOwner.get())
646     aFeatureOwner->eraseResults();
647   if (!aContext.IsNull()) {
648     aSel.Select(aNewShape, aContext); 
649   }
650 }
651
652 /// registers the name of the shape in the label (theID == 0) of sub label (theID is a tag)
653 /// if theID is zero, 
654 /// theOrientation is additional information about the positioning of edge relatively to face
655 ///    it is stored in the integer attribute of the edge sub-label: 
656 ///    -1 is out, 1 is in, 0 is not needed
657 static void registerSubShape(TDF_Label theMainLabel, TopoDS_Shape theShape,
658   const int theID, const FeaturePtr& theContextFeature, std::shared_ptr<Model_Document> theDoc,
659   std::string theAdditionalName, std::map<int, int>& theOrientations,
660   Handle(TDataStd_IntPackedMap) theRefs = Handle(TDataStd_IntPackedMap)(),
661   const int theOrientation = 0)
662 {
663   TDF_Label aLab = theID == 0 ? theMainLabel : theMainLabel.FindChild(theID);
664   if (theOrientation != 0) { // store the orientation of edge relatively to face if needed
665     TDataStd_Integer::Set(aLab, theOrientation);
666   }
667   TNaming_Builder aBuilder(aLab);
668   aBuilder.Generated(theShape);
669   std::stringstream aName;
670   aName<<theContextFeature->name()<<"/";
671   if (!theAdditionalName.empty())
672     aName<<theAdditionalName<<"/";
673   if (theShape.ShapeType() == TopAbs_FACE) aName<<"Face";
674   else if (theShape.ShapeType() == TopAbs_EDGE) aName<<"Edge";
675   else if (theShape.ShapeType() == TopAbs_VERTEX) aName<<"Vertex";
676
677   if (theRefs.IsNull()) {
678     aName<<theID;
679     if (theOrientation == 1)
680       aName<<"f";
681     else if (theOrientation == -1)
682       aName<<"r";
683   } else { // make a compisite name from all sub-elements indexes: "1_2_3_4"
684     TColStd_MapIteratorOfPackedMapOfInteger aRef(theRefs->GetMap());
685     for(; aRef.More(); aRef.Next()) {
686       aName<<"-"<<aRef.Key();
687       if (theOrientations.find(aRef.Key()) != theOrientations.end()) {
688         if (theOrientations[aRef.Key()] == 1)
689           aName<<"f";
690         else if (theOrientations[aRef.Key()] == -1)
691           aName<<"r";
692       }
693     }
694   }
695
696   theDoc->addNamingName(aLab, aName.str());
697   TDataStd_Name::Set(aLab, aName.str().c_str());
698 }
699
700 void Model_AttributeSelection::selectConstruction(
701   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape)
702 {
703   std::shared_ptr<Model_Document> aMyDoc = 
704     std::dynamic_pointer_cast<Model_Document>(owner()->document());
705   FeaturePtr aContextFeature = theContext->document()->feature(theContext);
706   CompositeFeaturePtr aComposite = 
707     std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aContextFeature);
708   const TopoDS_Shape& aSubShape = theSubShape->impl<TopoDS_Shape>();
709   if (!aComposite || aComposite->numberOfSubs() == 0) {
710     // saving of context is enough: result construction contains exactly the needed shape
711     TNaming_Builder aBuilder(selectionLabel());
712     aBuilder.Generated(aSubShape);
713     aMyDoc->addNamingName(selectionLabel(), theContext->data()->name());
714     TDataStd_Name::Set(selectionLabel(), theContext->data()->name().c_str());
715     return;
716   }
717   std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(owner()->data());
718   TDF_Label aLab = myRef.myRef->Label();
719   // identify the reuslts of sub-object of the composite by edges
720   // save type of the selected shape in integer attribute
721   TopAbs_ShapeEnum aShapeType = aSubShape.ShapeType();
722   TDataStd_Integer::Set(aLab, (int)aShapeType);
723   gp_Pnt aVertexPos;
724   TColStd_MapOfTransient allCurves;
725   if (aShapeType == TopAbs_VERTEX) { // compare positions
726     aVertexPos = BRep_Tool::Pnt(TopoDS::Vertex(aSubShape));
727   } else { 
728     for(TopExp_Explorer anEdgeExp(aSubShape, TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next()) {
729       TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExp.Current());
730       Standard_Real aFirst, aLast;
731       Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
732       allCurves.Add(aCurve);
733     }
734   }
735   // iterate and store the result ids of sub-elements and sub-elements to sub-labels
736   Handle(TDataStd_IntPackedMap) aRefs = TDataStd_IntPackedMap::Set(aLab);
737   std::map<int, int> anOrientations; //map from edges IDs to orientations of these edges in face
738   aRefs->Clear();
739   const int aSubNum = aComposite->numberOfSubs();
740   for(int a = 0; a < aSubNum; a++) {
741     FeaturePtr aSub = aComposite->subFeature(a);
742     const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = aSub->results();
743     std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResults.cbegin();
744     // there may be many shapes (circle and center): register if at least one is in selection
745     for(; aRes != aResults.cend(); aRes++) {
746       ResultConstructionPtr aConstr = 
747         std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aRes);
748       if (!aConstr->shape()) {
749         continue;
750       }
751       if (aShapeType == TopAbs_VERTEX) {
752         if (aConstr->shape()->isVertex()) { // compare vertices positions
753           const TopoDS_Shape& aVertex = aConstr->shape()->impl<TopoDS_Shape>();
754           gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVertex));
755           if (aPnt.IsEqual(aVertexPos, Precision::Confusion())) {
756             aRefs->Add(aComposite->subFeatureId(a));
757           }
758         } else { // get first or last vertex of the edge: last is stored with negative sign
759           const TopoDS_Shape& anEdge = aConstr->shape()->impl<TopoDS_Shape>();
760           int aDelta = kSTART_VERTEX_DELTA;
761           for(TopExp_Explorer aVExp(anEdge, TopAbs_VERTEX); aVExp.More(); aVExp.Next()) {
762             gp_Pnt aPnt = BRep_Tool::Pnt(TopoDS::Vertex(aVExp.Current()));
763             if (aPnt.IsEqual(aVertexPos, Precision::Confusion())) {
764               aRefs->Add(aDelta + aComposite->subFeatureId(a));
765               break;
766             }
767             aDelta += kSTART_VERTEX_DELTA;
768           }
769         }
770       } else {
771         if (aConstr->shape()->isEdge()) {
772           const TopoDS_Shape& aResShape = aConstr->shape()->impl<TopoDS_Shape>();
773           TopoDS_Edge anEdge = TopoDS::Edge(aResShape);
774           if (!anEdge.IsNull()) {
775             Standard_Real aFirst, aLast;
776             Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
777             if (allCurves.Contains(aCurve)) {
778               int anID = aComposite->subFeatureId(a);
779               aRefs->Add(anID);
780               if (aShapeType != TopAbs_EDGE) { // face needs the sub-edges on sub-labels
781                 // add edges to sub-label to support naming for edges selection
782                 TopExp_Explorer anEdgeExp(aSubShape, TopAbs_EDGE);
783                 for(; anEdgeExp.More(); anEdgeExp.Next()) {
784                   TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExp.Current());
785                   Standard_Real aFirst, aLast;
786                   Handle(Geom_Curve) aFaceCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
787                   if (aFaceCurve == aCurve) {
788                     int anOrient = edgeOrientation(aSubShape, anEdge);
789                     anOrientations[anID] = anOrient;
790                     registerSubShape(
791                       selectionLabel(), anEdge, anID, aContextFeature, aMyDoc, "", anOrientations,
792                       Handle(TDataStd_IntPackedMap)(), anOrient);
793                   }
794                 }
795               } else { // put vertices of the selected edge to sub-labels
796                 // add edges to sub-label to support naming for edges selection
797                 TopExp_Explorer anEdgeExp(aSubShape, TopAbs_VERTEX);
798                 int aTagIndex = anID + kSTART_VERTEX_DELTA;
799                 for(; anEdgeExp.More(); anEdgeExp.Next(), aTagIndex += kSTART_VERTEX_DELTA) {
800                   TopoDS_Vertex aV = TopoDS::Vertex(anEdgeExp.Current());
801
802                   std::stringstream anAdditionalName; 
803                   registerSubShape(
804                     selectionLabel(), aV, aTagIndex, aContextFeature, aMyDoc, "", anOrientations);
805                 }
806               }
807             }
808           }
809         }
810       }
811     }
812   }
813   // store the selected as primitive
814   TNaming_Builder aBuilder(selectionLabel());
815   aBuilder.Generated(aSubShape);
816     registerSubShape(
817       selectionLabel(), aSubShape, 0, aContextFeature, aMyDoc, "", anOrientations, aRefs); 
818 }
819
820 bool Model_AttributeSelection::selectPart(
821   const ResultPtr& theContext, const std::shared_ptr<GeomAPI_Shape>& theSubShape,
822   const bool theUpdate)
823 {
824   ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(theContext);
825   if (!aPart.get() || !aPart->isActivated())
826     return true; // postponed naming
827   if (theUpdate) {
828     Handle(TDataStd_Integer) anIndex;
829     if (selectionLabel().FindAttribute(TDataStd_Integer::GetID(), anIndex)) { // by internal selection
830       if (anIndex->Get() > 0) {
831         // update the selection by index
832         return aPart->updateInPart(anIndex->Get());
833       } else {
834         return true; // nothing to do, referencing just by name
835       }
836     }
837     return true; // nothing to do, referencing just by name
838   }
839   // store the shape (in case part is not loaded it should be usefull
840   TopoDS_Shape aShape;
841   std::string aName = theContext->data()->name();
842   if (!theSubShape.get() || theSubShape->isNull()) {// the whole part shape is selected
843     aShape = theContext->shape()->impl<TopoDS_Shape>();
844   } else {
845     aShape = theSubShape->impl<TopoDS_Shape>();
846     int anIndex;
847     aName += "/" + aPart->nameInPart(theSubShape, anIndex);
848     TDataStd_Integer::Set(selectionLabel(), anIndex);
849   }
850   TNaming_Builder aBuilder(selectionLabel());
851   aBuilder.Select(aShape, aShape);
852   // identify by name in the part
853   TDataStd_Name::Set(selectionLabel(), aName.c_str());
854   return !aName.empty();
855 }
856
857 TDF_Label Model_AttributeSelection::selectionLabel()
858 {
859   return myRef.myRef->Label().FindChild(1);
860 }
861
862 std::string Model_AttributeSelection::namingName(const std::string& theDefaultName)
863 {
864   std::string aName("");
865   if(!this->isInitialized())
866     return !theDefaultName.empty() ? theDefaultName : aName;
867   Handle(TDataStd_Name) anAtt;
868   if(selectionLabel().FindAttribute(TDataStd_Name::GetID(), anAtt)) {
869     aName = TCollection_AsciiString(anAtt->Get()).ToCString();
870     return aName;
871   }
872
873   std::shared_ptr<GeomAPI_Shape> aSubSh = value();
874   ResultPtr aCont = context();
875
876   Model_SelectionNaming aSelNaming(selectionLabel());
877   return aSelNaming.namingName(aCont, aSubSh, theDefaultName);
878 }
879
880 // type ::= COMP | COMS | SOLD | SHEL | FACE | WIRE | EDGE | VERT
881 void Model_AttributeSelection::selectSubShape(
882   const std::string& theType, const std::string& theSubShapeName)
883 {
884   if(theSubShapeName.empty() || theType.empty()) return;
885
886   // check this is Part-name: 2 delimiters in the name
887   std::size_t aPartEnd = theSubShapeName.find('/');
888   if (aPartEnd != string::npos && aPartEnd != theSubShapeName.rfind('/')) {
889     std::string aPartName = theSubShapeName.substr(0, aPartEnd);
890     ObjectPtr aFound = owner()->document()->objectByName(ModelAPI_ResultPart::group(), aPartName);
891     if (aFound.get()) { // found such part, so asking it for the name
892       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aFound);
893       string aNameInPart = theSubShapeName.substr(aPartEnd + 1);
894       int anIndex;
895       std::shared_ptr<GeomAPI_Shape> aSelected = aPart->shapeInPart(aNameInPart, theType, anIndex);
896       if (aSelected.get()) {
897         setValue(aPart, aSelected);
898         TDataStd_Integer::Set(selectionLabel(), anIndex);
899         return;
900       }
901     }
902   }
903
904   Model_SelectionNaming aSelNaming(selectionLabel());
905   std::shared_ptr<Model_Document> aDoc = 
906     std::dynamic_pointer_cast<Model_Document>(owner()->document());
907   std::shared_ptr<GeomAPI_Shape> aShapeToBeSelected;
908   ResultPtr aCont;
909   if (aSelNaming.selectSubShape(theType, theSubShapeName, aDoc, aShapeToBeSelected, aCont)) {
910     setValue(aCont, aShapeToBeSelected);
911   }
912 }
913
914 int Model_AttributeSelection::Id()
915 {
916   int anID = 0;
917   std::shared_ptr<GeomAPI_Shape> aSelection = value();
918   std::shared_ptr<GeomAPI_Shape> aContext = context()->shape();
919   TopoDS_Shape aMainShape = aContext->impl<TopoDS_Shape>();
920   const TopoDS_Shape& aSubShape = aSelection->impl<TopoDS_Shape>();
921   // searching for the latest main shape
922   if (aSelection && !aSelection->isNull() &&
923     aContext   && !aContext->isNull())
924   {
925     std::shared_ptr<Model_Document> aDoc =
926       std::dynamic_pointer_cast<Model_Document>(context()->document());
927     if (aDoc.get()) {
928       Handle(TNaming_NamedShape) aNS = TNaming_Tool::NamedShape(aMainShape, aDoc->generalLabel());
929       if (!aNS.IsNull()) {
930         aMainShape = TNaming_Tool::CurrentShape(aNS);
931       }
932     }
933
934     TopTools_IndexedMapOfShape aSubShapesMap;
935     TopExp::MapShapes(aMainShape, aSubShapesMap);
936     anID = aSubShapesMap.FindIndex(aSubShape);
937   }
938   return anID;
939 }