Salome HOME
0c312f3955c162af9e8bbc0ddeb993efa422d02e
[modules/shaper.git] / src / ModelAPI / ModelAPI_Tools.cpp
1 // Copyright (C) 2014-2019  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 email : webmaster.salome@opencascade.com
18 //
19
20 #include "ModelAPI_Tools.h"
21 #include <ModelAPI_Session.h>
22 #include <ModelAPI_CompositeFeature.h>
23 #include <ModelAPI_Document.h>
24 #include <ModelAPI_Object.h>
25 #include <ModelAPI_AttributeDouble.h>
26 #include <ModelAPI_AttributeSelectionList.h>
27 #include <ModelAPI_ResultBody.h>
28 #include <ModelAPI_ResultParameter.h>
29 #include <ModelAPI_ResultPart.h>
30 #include <ModelAPI_AttributeDocRef.h>
31 #include <ModelAPI_Validator.h>
32 #include <ModelAPI_AttributeIntArray.h>
33 #include <ModelAPI_ResultConstruction.h>
34 #include <list>
35 #include <map>
36 #include <iostream>
37 #include <sstream>
38
39 #include <Events_Loop.h>
40 #include <ModelAPI_Events.h>
41
42 #define RECURSE_TOP_LEVEL 50
43
44 //#define DEBUG_REMOVE_FEATURES
45 //#define DEBUG_REMOVE_FEATURES_RECURSE
46 //#define DEBUG_CYCLING_1550
47
48 #ifdef DEBUG_REMOVE_FEATURES_RECURSE
49 #include <sstream>
50 std::string getFeatureInfo(FeaturePtr theFeature)
51 {
52   if (!theFeature.get())
53     return "";
54   //std::ostringstream aPtrStr;
55   //aPtrStr << "[" << theFeature.get() << "] ";
56   std::string aFeatureInfo = /*aPtrStr.str() + */theFeature->name();
57   CompositeFeaturePtr aComposite = ModelAPI_Tools::compositeOwner(theFeature);
58   if (aComposite.get()) {
59       aFeatureInfo = aFeatureInfo + "[in " + aComposite->name() + "]";
60   }
61   return aFeatureInfo;
62 }
63 #endif
64
65 #ifdef DEBUG_REMOVE_FEATURES
66 void printMapInfo(const std::map<FeaturePtr, std::set<FeaturePtr> >& theMainList,
67                   const std::string& thePrefix)
68 {
69   std::map<FeaturePtr, std::set<FeaturePtr> >::const_iterator aMainIt = theMainList.begin(),
70                                                               aMainLast = theMainList.end();
71   std::string anInfo;
72   for (; aMainIt != aMainLast; aMainIt++) {
73     FeaturePtr aMainListFeature = aMainIt->first;
74     std::set<FeaturePtr> aMainRefList = aMainIt->second;
75     std::set<FeaturePtr>::const_iterator anIt = aMainRefList.begin(), aLast = aMainRefList.end();
76     std::string aRefsInfo;
77     for (; anIt != aLast; anIt++) {
78       aRefsInfo += (*anIt)->name().c_str();
79       if (anIt != aLast)
80         aRefsInfo += ", ";
81     }
82     if (!aRefsInfo.empty()) {
83       anInfo = anInfo + aMainListFeature->name().c_str() + ": " + aRefsInfo + "\n";
84     }
85   }
86   std::cout << thePrefix.c_str() << " [feature: references to]: \n" << anInfo.c_str() << std::endl;
87 }
88
89 void printListInfo(const std::set<FeaturePtr>& theMainList,
90                   const std::string& thePrefix)
91 {
92   std::set<FeaturePtr>::const_iterator aMainIt = theMainList.begin(),
93                                        aMainLast = theMainList.end();
94   std::string anInfo;
95   for (; aMainIt != aMainLast; aMainIt++) {
96     FeaturePtr aRefFeature = *aMainIt;
97     anInfo += aRefFeature->name().c_str();
98     if (aMainIt != aMainLast)
99       anInfo += ", ";
100   }
101   std::cout << thePrefix.c_str() << ": " << anInfo.c_str() << std::endl;
102 }
103 #endif
104
105 namespace ModelAPI_Tools {
106
107 std::shared_ptr<GeomAPI_Shape> shape(const ResultPtr& theResult)
108 {
109   return theResult->shape();
110 }
111
112 // LCOV_EXCL_START
113 const char* toString(ModelAPI_ExecState theExecState)
114 {
115   switch (theExecState) {
116   case ModelAPI_StateDone: return "Done";
117   case ModelAPI_StateMustBeUpdated: return "Must be updated";
118   case ModelAPI_StateExecFailed: return "Execution failed";
119   case ModelAPI_StateInvalidArgument: return "Invalid argument";
120   case ModelAPI_StateNothing: return "Empty state";
121   default: return "Unknown ExecState.";
122   }
123 }
124
125 std::string getFeatureError(const FeaturePtr& theFeature)
126 {
127   std::string anError;
128   if (!theFeature.get() || !theFeature->data()->isValid() || theFeature->isAction())
129     return anError;
130
131   // to be removed later, this error should be got from the feature
132   if (theFeature->data()->execState() == ModelAPI_StateDone ||
133       theFeature->data()->execState() == ModelAPI_StateMustBeUpdated)
134     return anError;
135
136   // set error indication
137   anError = theFeature->error();
138   if (anError.empty()) {
139     bool isDone = ( theFeature->data()->execState() == ModelAPI_StateDone
140                  || theFeature->data()->execState() == ModelAPI_StateMustBeUpdated );
141     if (!isDone) {
142       anError = toString(theFeature->data()->execState());
143       // If the feature is Composite and error is StateInvalidArgument,
144       // error text should include error of first invalid sub-feature. Otherwise
145       // it is not clear what is the reason of the invalid argument.
146       if (theFeature->data()->execState() == ModelAPI_StateInvalidArgument) {
147         CompositeFeaturePtr aComposite =
148                     std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
149         if (aComposite) {
150           for (int i = 0, aSize = aComposite->numberOfSubs(); i < aSize; i++) {
151             FeaturePtr aSubFeature = aComposite->subFeature(i);
152             std::string aSubFeatureError = getFeatureError(aSubFeature);
153             if (!aSubFeatureError.empty()) {
154               anError = anError + " in " + aSubFeature->getKind() + ".\n" + aSubFeatureError;
155               break;
156             }
157           }
158         }
159       }
160     }
161   }
162
163   return anError;
164 }
165 // LCOV_EXCL_STOP
166
167 ObjectPtr objectByName(const DocumentPtr& theDocument, const std::string& theGroup,
168                        const std::string& theName)
169 {
170   for (int anIndex = 0; anIndex < theDocument->size(theGroup); ++anIndex) {
171     ObjectPtr anObject = theDocument->object(theGroup, anIndex);
172     if (anObject->data()->name() == theName)
173       return anObject;
174   }
175   // not found
176   return ObjectPtr();
177 }
178
179 bool findVariable(const DocumentPtr& theDocument, FeaturePtr theSearcher,
180                   const std::string& theName, double& outValue, ResultParameterPtr& theParam)
181 {
182   ObjectPtr aParamObj = objectByName(theDocument, ModelAPI_ResultParameter::group(), theName);
183   theParam = std::dynamic_pointer_cast<ModelAPI_ResultParameter>(aParamObj);
184   if (!theParam.get())
185     return false;
186   // avoid usage of parameters created later than the initial parameter
187
188   if (theSearcher.get()) {
189     FeaturePtr aParamFeat = theDocument->feature(theParam);
190     if (aParamFeat == theSearcher || theDocument->isLater(aParamFeat, theSearcher))
191       return false;
192   }
193   AttributeDoublePtr aValueAttribute = theParam->data()->real(ModelAPI_ResultParameter::VALUE());
194   outValue = aValueAttribute->value();
195   return true;
196 }
197
198 bool findVariable(FeaturePtr theSearcher, const std::string& theName, double& outValue,
199                   ResultParameterPtr& theParam, const DocumentPtr& theDocument)
200 {
201   SessionPtr aSession = ModelAPI_Session::get();
202   std::list<DocumentPtr> aDocList;
203   DocumentPtr aDocument = theDocument.get() ? theDocument : aSession->activeDocument();
204   if (findVariable(aDocument, theSearcher, theName, outValue, theParam))
205     return true;
206   DocumentPtr aRootDocument = aSession->moduleDocument();
207   if (aDocument != aRootDocument) {
208     // any parameters in PartSet is okindependently on the Part position (issu #1504)
209     if (findVariable(aRootDocument, FeaturePtr(), theName, outValue, theParam))
210       return true;
211   }
212   return false;
213 }
214
215 ResultPtr findPartResult(const DocumentPtr& theMain, const DocumentPtr& theSub)
216 {
217   // to optimize and avoid of crash on partset document close
218   // (don't touch the sub-document structure)
219   if (theMain != theSub) {
220     for (int a = theMain->size(ModelAPI_ResultPart::group()) - 1; a >= 0; a--) {
221       ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(
222           theMain->object(ModelAPI_ResultPart::group(), a));
223       if (aPart && aPart->data()->document(ModelAPI_ResultPart::DOC_REF())->value() == theSub) {
224         return aPart;
225       }
226     }
227   }
228   return ResultPtr();
229 }
230
231 FeaturePtr findPartFeature(const DocumentPtr& theMain, const DocumentPtr& theSub)
232 {
233   // to optimize and avoid of crash on partset document close
234   // (don't touch the sub-document structure)
235   if (theMain != theSub) {
236     // iteration from top to bottom to avoid finding the movement documents before the original
237     int aSize = theMain->size(ModelAPI_Feature::group());
238     for (int a = 0; a < aSize; a++) {
239       FeaturePtr aPartFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(
240           theMain->object(ModelAPI_Feature::group(), a));
241       if (aPartFeat.get()) {
242         const std::list<std::shared_ptr<ModelAPI_Result> >& aResList = aPartFeat->results();
243         std::list<std::shared_ptr<ModelAPI_Result> >::const_iterator aRes = aResList.begin();
244         for(; aRes != aResList.end(); aRes++) {
245           ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aRes);
246           if (aPart.get()) {
247             if (aPart->isActivated() && aPart->partDoc() == theSub)
248               return aPartFeat;
249           } else break; // if the first is not Part, others are also not
250         }
251       }
252     }
253   }
254   return FeaturePtr();
255 }
256
257 CompositeFeaturePtr compositeOwner(const FeaturePtr& theFeature)
258 {
259   if (theFeature.get() && theFeature->data() && theFeature->data()->isValid()) {
260     const std::set<std::shared_ptr<ModelAPI_Attribute> >& aRefs = theFeature->data()->refsToMe();
261     std::set<std::shared_ptr<ModelAPI_Attribute> >::const_iterator aRefIter = aRefs.begin();
262     for(; aRefIter != aRefs.end(); aRefIter++) {
263       CompositeFeaturePtr aComp = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>
264         ((*aRefIter)->owner());
265       if (aComp.get() && aComp->data()->isValid() && aComp->isSub(theFeature))
266         return aComp;
267     }
268   }
269   return CompositeFeaturePtr(); // not found
270 }
271
272 ResultBodyPtr bodyOwner(const ResultPtr& theSub, const bool theRoot)
273 {
274   if (theSub.get()) {
275     ObjectPtr aParent = theSub->document()->parent(theSub);
276     if (aParent.get()) {
277       if (theRoot) { // try to find parent of parent
278         ResultPtr aResultParent = std::dynamic_pointer_cast<ModelAPI_Result>(aParent);
279         ResultBodyPtr aGrandParent = bodyOwner(aResultParent, true);
280         if (aGrandParent.get())
281           aParent = aGrandParent;
282       }
283       return std::dynamic_pointer_cast<ModelAPI_ResultBody>(aParent);
284     }
285   }
286   return ResultBodyPtr(); // not found
287 }
288
289 int bodyIndex(const ResultPtr& theSub)
290 {
291   int anIndex = -1;
292   ResultBodyPtr aParent = bodyOwner(theSub);
293   if (aParent.get()) {
294     ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theSub);
295     if (aBody.get() && aParent->isSub(aBody, anIndex))
296       return anIndex;
297   }
298   return anIndex; // not found
299 }
300
301 bool hasSubResults(const ResultPtr& theResult)
302 {
303   ResultBodyPtr aCompSolid = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theResult);
304   return aCompSolid.get() && aCompSolid->numberOfSubs() > 0;
305 }
306
307 void allSubs(const ResultBodyPtr& theResult, std::list<ResultPtr>& theResults,
308              const bool theLowerOnly) {
309   // iterate sub-bodies of compsolid
310   ResultBodyPtr aComp = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theResult);
311   if (aComp.get()) {
312     int aNumSub = aComp->numberOfSubs();
313     for (int a = 0; a < aNumSub; a++) {
314       ResultBodyPtr aSub = aComp->subResult(a);
315       if (!theLowerOnly || aSub->numberOfSubs() == 0)
316         theResults.push_back(aSub);
317       allSubs(aSub, theResults);
318     }
319   }
320 }
321
322 void allResults(const FeaturePtr& theFeature, std::list<ResultPtr>& theResults)
323 {
324   if (!theFeature.get()) // safety: for empty feature no results
325     return;
326   const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = theFeature->results();
327   std::list<ResultPtr>::const_iterator aRIter = aResults.begin();
328   for (; aRIter != aResults.cend(); aRIter++) {
329     theResults.push_back(*aRIter);
330     ResultBodyPtr aResult = std::dynamic_pointer_cast<ModelAPI_ResultBody>(*aRIter);
331     allSubs(aResult, theResults);
332   }
333 }
334
335 //******************************************************************
336 bool allDocumentsActivated(std::string& theNotActivatedNames)
337 {
338   theNotActivatedNames = "";
339   bool anAllPartActivated = true;
340
341   DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
342   int aSize = aRootDoc->size(ModelAPI_ResultPart::group());
343   for (int i = 0; i < aSize; i++) {
344     ObjectPtr aObject = aRootDoc->object(ModelAPI_ResultPart::group(), i);
345     ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObject);
346     if (!aPart->isActivated()) {
347       anAllPartActivated = false;
348       if (!theNotActivatedNames.empty())
349         theNotActivatedNames += ", ";
350       theNotActivatedNames += aObject->data()->name().c_str();
351     }
352   }
353   return anAllPartActivated;
354 }
355
356 bool removeFeaturesAndReferences(const std::set<FeaturePtr>& theFeatures,
357                                  const bool theFlushRedisplay,
358                                  const bool theUseComposite,
359                                  const bool theUseRecursion)
360 {
361 #ifdef DEBUG_REMOVE_FEATURES
362   printListInfo(theFeatures, "selection: ");
363 #endif
364
365   std::map<FeaturePtr, std::set<FeaturePtr> > aReferences;
366   ModelAPI_Tools::findAllReferences(theFeatures, aReferences, theUseComposite, theUseRecursion);
367 #ifdef DEBUG_REMOVE_FEATURES
368   printMapInfo(aReferences, "allDependencies: ");
369 #endif
370
371   std::set<FeaturePtr> aFeaturesRefsTo;
372   ModelAPI_Tools::findRefsToFeatures(theFeatures, aReferences, aFeaturesRefsTo);
373 #ifdef DEBUG_REMOVE_FEATURES
374   printListInfo(aFeaturesRefsTo, "references: ");
375 #endif
376
377   std::set<FeaturePtr> aFeatures = theFeatures;
378   if (!aFeaturesRefsTo.empty())
379     aFeatures.insert(aFeaturesRefsTo.begin(), aFeaturesRefsTo.end());
380 #ifdef DEBUG_REMOVE_FEATURES
381   printListInfo(aFeatures, "removeFeatures: ");
382 #endif
383
384   return ModelAPI_Tools::removeFeatures(aFeatures, false);
385 }
386
387 //***********************************************************************
388 bool removeFeatures(const std::set<FeaturePtr>& theFeatures,
389                     const bool theFlushRedisplay)
390 {
391   bool isDone = false;
392   std::set<FeaturePtr>::const_iterator anIt = theFeatures.begin(),
393                                        aLast = theFeatures.end();
394   for (; anIt != aLast; anIt++) {
395     FeaturePtr aFeature = *anIt;
396     if (aFeature.get()) {
397       DocumentPtr aDoc = aFeature->document();
398       // flush REDISPLAY signal after remove feature
399       aDoc->removeFeature(aFeature);
400       isDone = true;
401     }
402   }
403   if (isDone && theFlushRedisplay) {
404     // the redisplay signal should be flushed in order to erase
405     // the feature presentation in the viewer
406     // if should be done after removeFeature() of document
407     Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
408   }
409   return true;
410 }
411
412 //***********************************************************************
413 // Fills the references list by all references of the feature from the references map.
414 // This is a recusive method to find references by next found feature in the map of references.
415 // \param theFeature a feature to find references
416 // \param theReferencesMap a map of references
417 // \param theReferences an out container of references
418 void addRefsToFeature(const FeaturePtr& theFeature,
419                       const std::map<FeaturePtr, std::set<FeaturePtr> >& theReferencesMap,
420                       int theRecLevel,
421                       std::set<FeaturePtr>& theReferences)
422 {
423   if (theRecLevel > RECURSE_TOP_LEVEL)
424     return;
425   theRecLevel++;
426
427   if (theReferencesMap.find(theFeature) == theReferencesMap.end())
428     return; // this feature is not in the selection list, so exists without references to it
429   std::set<FeaturePtr> aMainReferences = theReferencesMap.at(theFeature);
430
431   std::set<FeaturePtr>::const_iterator anIt = aMainReferences.begin(),
432                                        aLast = aMainReferences.end();
433   for (; anIt != aLast; anIt++) {
434     FeaturePtr aRefFeature = *anIt;
435     if (theReferences.find(aRefFeature) == theReferences.end()) {
436       addRefsToFeature(aRefFeature, theReferencesMap, theRecLevel, theReferences);
437       theReferences.insert(aRefFeature);
438     }
439   }
440 }
441
442 // For each feature from the feature list it searches references to the feature and append them
443 // to the references map. This is a recusive method.
444 // \param theFeature a feature to find references
445 // \param theReferencesMap a map of references
446 // \param theReferences an out container of references
447 void findReferences(const std::set<FeaturePtr>& theFeatures,
448                     std::map<FeaturePtr, std::set<FeaturePtr> >& theReferences,
449                     const bool theUseComposite, const bool theUseRecursion, int theRecLevel)
450 {
451   if (theRecLevel > RECURSE_TOP_LEVEL)
452     return;
453   theRecLevel++;
454   std::set<FeaturePtr>::const_iterator anIt = theFeatures.begin(),
455                                         aLast = theFeatures.end();
456   for (; anIt != aLast; anIt++) {
457     FeaturePtr aFeature = *anIt;
458     if (aFeature.get() && theReferences.find(aFeature) == theReferences.end()) {
459       DocumentPtr aSelFeatureDoc = aFeature->document();
460       std::set<FeaturePtr> aSelRefFeatures;
461       aSelFeatureDoc->refsToFeature(aFeature, aSelRefFeatures, false/*do not emit signals*/);
462       if (theUseComposite) { // do not filter selection
463         theReferences[aFeature] = aSelRefFeatures;
464       }
465       else { // filter references to skip composition features of the current feature
466         std::set<FeaturePtr> aFilteredFeatures;
467         std::set<FeaturePtr>::const_iterator anIt = aSelRefFeatures.begin(),
468                                              aLast = aSelRefFeatures.end();
469         for (; anIt != aLast; anIt++) {
470           FeaturePtr aCFeature = *anIt;
471           CompositeFeaturePtr aComposite =
472             std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aCFeature);
473           if (aComposite.get() && aComposite->isSub(aFeature))
474             continue; /// composite of the current feature should be skipped
475           aFilteredFeatures.insert(aCFeature);
476         }
477         theReferences[aFeature] = aFilteredFeatures;
478       }
479       if (theUseRecursion) {
480 #ifdef DEBUG_CYCLING_1550
481         findReferences(aSelRefFeatures, theReferences, theUseComposite,
482                        theUseRecursion, theRecLevel);
483 #else
484         findReferences(theReferences[aFeature], theReferences, theUseComposite, theUseRecursion,
485                        theRecLevel);
486 #endif
487       }
488     }
489   }
490 }
491
492 void findAllReferences(const std::set<FeaturePtr>& theFeatures,
493                        std::map<FeaturePtr, std::set<FeaturePtr> >& theReferences,
494                        const bool theUseComposite,
495                        const bool theUseRecursion)
496 {
497   // For dependencies, find main_list:
498   // sk_1(ext_1, vertex_1)
499   // ext_1(bool_1, sk_3)
500   // vertex_1()
501   // sk_2(ext_2)
502   // ext_2(bool_2)
503   // sk_3()
504   // Information: bool_1 is not selected, ext_2(bool_2) exists
505   // find all referenced features
506   std::map<FeaturePtr, std::set<FeaturePtr> > aMainList;
507   int aRecLevel = 0;
508   findReferences(theFeatures, aMainList, theUseComposite, theUseRecursion, aRecLevel);
509
510 #ifdef DEBUG_REMOVE_FEATURES
511   printMapInfo(aMainList, "firstDependencies");
512 #endif
513   // find all dependencies for each object:
514   // sk_1(ext_1, vertex_1) + (sk_3, bool_1)
515   // ext_1(bool_1, sk_3)
516   // vertex_1()
517   // sk_2(ext_2) + (bool_1)
518   // ext_2(bool_1)
519   // sk_3()
520   std::map<FeaturePtr, std::set<FeaturePtr> >::const_iterator aMainIt = aMainList.begin(),
521                                                               aMainLast = aMainList.end();
522   for (; aMainIt != aMainLast; aMainIt++) {
523     FeaturePtr aMainListFeature = aMainIt->first;
524
525     //std::string aName = aMainListFeature->name();
526     std::set<FeaturePtr> aMainRefList = aMainIt->second;
527
528 #ifdef DEBUG_REMOVE_FEATURES_RECURSE
529     char aBuf[50];
530     int n = sprintf(aBuf, "%d", aMainRefList.size());
531     std::string aSize(aBuf);
532     std::cout << "_findAllReferences for the Feature: " << getFeatureInfo(aMainListFeature)
533               << ", references size = " << aSize << std::endl;
534 #endif
535     std::set<FeaturePtr>::const_iterator anIt = aMainRefList.begin(),
536                                          aLast = aMainRefList.end();
537     std::set<FeaturePtr> aResultRefList;
538     aResultRefList.insert(aMainRefList.begin(), aMainRefList.end());
539     for (; anIt != aLast; anIt++) {
540       FeaturePtr aFeature = *anIt;
541       int aRecLevel = 0;
542 #ifdef DEBUG_REMOVE_FEATURES_RECURSE
543       std::cout << " Ref: " << getFeatureInfo(aFeature) << std::endl;
544 #endif
545       aRecLevel++;
546       addRefsToFeature(aFeature, aMainList,
547                        aRecLevel, aResultRefList/*aMainRefList*/);
548     }
549     theReferences[aMainListFeature] = aResultRefList;
550   }
551 #ifdef DEBUG_REMOVE_FEATURES_RECURSE
552     std::cout << std::endl;
553 #endif
554
555 #ifdef DEBUG_REMOVE_FEATURES
556   printMapInfo(theReferences, "allDependencies");
557 #endif
558 }
559
560 void findRefsToFeatures(const std::set<FeaturePtr>& theFeatures,
561                         const std::map<FeaturePtr, std::set<FeaturePtr> >& theReferences,
562                         std::set<FeaturePtr>& theFeaturesRefsTo)
563 {
564   std::set<FeaturePtr>::const_iterator anIt = theFeatures.begin(),
565                                        aLast = theFeatures.end();
566   for (; anIt != aLast; anIt++) {
567     FeaturePtr aFeature = *anIt;
568     if (theReferences.find(aFeature) == theReferences.end())
569       continue;
570     std::set<FeaturePtr> aRefList = theReferences.at(aFeature);
571     std::set<FeaturePtr>::const_iterator aRefIt = aRefList.begin(), aRefLast = aRefList.end();
572     for (; aRefIt != aRefLast; aRefIt++) {
573       FeaturePtr aRefFeature = *aRefIt;
574       CompositeFeaturePtr aComposite =
575         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aRefFeature);
576       if (aComposite.get() && aComposite->isSub(aFeature))
577         continue; /// composite of the current feature should not be removed
578
579       if (theFeatures.find(aRefFeature) == theFeatures.end() && // it is not selected
580           theFeaturesRefsTo.find(aRefFeature) == theFeaturesRefsTo.end()) // it is not added
581         theFeaturesRefsTo.insert(aRefFeature);
582     }
583   }
584 }
585
586 void getConcealedResults(const FeaturePtr& theFeature,
587                          std::list<std::shared_ptr<ModelAPI_Result> >& theResults)
588 {
589   SessionPtr aSession = ModelAPI_Session::get();
590
591   std::list<std::pair<std::string, std::list<std::shared_ptr<ModelAPI_Object> > > > aRefs;
592   theFeature->data()->referencesToObjects(aRefs);
593   std::list<std::pair<std::string, std::list<ObjectPtr> > >::const_iterator
594                                                   anIt = aRefs.begin(), aLast = aRefs.end();
595   std::set<ResultPtr> alreadyThere; // to avoid duplications
596   for (; anIt != aLast; anIt++) {
597     if (!aSession->validators()->isConcealed(theFeature->getKind(), anIt->first))
598       continue; // use only concealed attributes
599     std::list<ObjectPtr> anObjects = (*anIt).second;
600     std::list<ObjectPtr>::const_iterator anOIt = anObjects.begin(), anOLast = anObjects.end();
601     for (; anOIt != anOLast; anOIt++) {
602       ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(*anOIt);
603       if (aResult && aResult->isConcealed()) {
604         if (alreadyThere.find(aResult) == alreadyThere.end()) // issue 1712, avoid duplicates
605           alreadyThere.insert(aResult);
606         else continue;
607         theResults.push_back(aResult);
608       }
609     }
610   }
611 }
612
613 std::pair<std::string, bool> getDefaultName(const std::shared_ptr<ModelAPI_Result>& theResult,
614                                             const bool theInherited)
615 {
616   typedef std::list< std::pair < std::string, std::list<ObjectPtr> > > ListOfReferences;
617
618   SessionPtr aSession = ModelAPI_Session::get();
619
620   ResultBodyPtr anOwnerRes = bodyOwner(theResult);
621   if (anOwnerRes) {
622     // names of sub-solids in CompSolid should be default (for example,
623     // result of boolean operation 'Boolean_1_1' is a CompSolid which is renamed to 'MyBOOL',
624     // however, sub-elements of 'MyBOOL' should be named 'Boolean_1_1_1', 'Boolean_1_1_2' etc.)
625     std::ostringstream aDefaultName;
626     aDefaultName << getDefaultName(anOwnerRes).first;
627     aDefaultName << "_" << (bodyIndex(theResult) + 1);
628     return std::pair<std::string, bool>(aDefaultName.str(), false);
629   }
630
631   FeaturePtr anOwner = ModelAPI_Feature::feature(theResult->data()->owner());
632   DataPtr aData = anOwner->data();
633
634   ListOfReferences aReferences;
635   // find first result with user-defined name
636   ListOfReferences::const_iterator aFoundRef = aReferences.end();
637   if (theInherited) {
638     aData->referencesToObjects(aReferences);
639
640     for (ListOfReferences::const_iterator aRefIt = aReferences.begin();
641          aRefIt != aReferences.end(); ++aRefIt) {
642       bool isConcealed = aSession->validators()->isConcealed(anOwner->getKind(), aRefIt->first);
643       bool isMainArg = isConcealed &&
644                        aSession->validators()->isMainArgument(anOwner->getKind(), aRefIt->first);
645       if (isConcealed) {
646         // check the referred object is a Body
647         // (for example, ExtrusionCut has a sketch as a first attribute which is concealing)
648         bool isBody = aRefIt->second.size() > 1 || (aRefIt->second.size() == 1 &&
649                       aRefIt->second.front()->groupName() == ModelAPI_ResultBody::group());
650         if (isBody && (isMainArg || aFoundRef == aReferences.end() ||
651             aData->isPrecedingAttribute(aRefIt->first, aFoundRef->first)))
652           aFoundRef = aRefIt;
653
654         if (isMainArg)
655           break;
656       }
657     }
658   }
659   // get the result number in the feature
660   int anIndexInOwner = 0;
661   const std::list<ResultPtr>& anOwnerResults = anOwner->results();
662   std::list<ResultPtr>::const_iterator aResIt = anOwnerResults.cbegin();
663   for(; aResIt != anOwnerResults.cend(); aResIt++) {
664     if(*aResIt == theResult)
665       break;
666     anIndexInOwner++;
667   }
668
669   // find an object which is concealed by theResult
670   if (aFoundRef != aReferences.end() && !aFoundRef->second.empty()) {
671     // store number of references for each object
672     std::map<ResultPtr, int> aNbRefToObject;
673     // search the object by result index
674     std::list<ObjectPtr>::const_iterator anObjIt = aFoundRef->second.begin();
675     int aResultIndex = anIndexInOwner;
676     while (--aResultIndex >= 0) {
677       ResultPtr aCurRes = std::dynamic_pointer_cast<ModelAPI_Result>(*anObjIt);
678       ResultBodyPtr aParentBody = ModelAPI_Tools::bodyOwner(aCurRes);
679       if (aParentBody)
680         aCurRes = aParentBody;
681       if (aNbRefToObject.find(aCurRes) == aNbRefToObject.end())
682         aNbRefToObject[aCurRes] = 1;
683       else
684         aNbRefToObject[aCurRes] += 1;
685
686       ++anObjIt;
687       if (anObjIt == aFoundRef->second.end()) {
688         anObjIt = aFoundRef->second.begin();
689         break;
690       }
691     }
692     // check the result is a Body
693     if ((*anObjIt)->groupName() == ModelAPI_ResultBody::group()) {
694       // check the result is part of CompSolid
695       ResultPtr anObjRes = std::dynamic_pointer_cast<ModelAPI_Result>(*anObjIt);
696       ResultBodyPtr aParentBody = ModelAPI_Tools::bodyOwner(anObjRes);
697       if (aParentBody)
698         anObjRes = aParentBody;
699
700       // return name of reference result only if it has been renamed by the user,
701       // in other case compose a default name
702       if (anObjRes->data()->hasUserDefinedName()) {
703         std::stringstream aName;
704         aName << anObjRes->data()->name();
705         std::map<ResultPtr, int>::iterator aFound = aNbRefToObject.find(anObjRes);
706         if (aFound != aNbRefToObject.end()) {
707           // to generate unique name, add suffix if there are several results
708           // referring to the same shape
709           aName << "_" << aFound->second + 1;
710         }
711         return std::pair<std::string, bool>(aName.str(), true);
712       }
713     }
714   }
715
716   // compose default name by the name of the feature and the index of result
717   std::stringstream aDefaultName;
718   aDefaultName << anOwner->name();
719   // if there are several results (issue #899: any number of result),
720   // add unique prefix starting from second
721   if (anIndexInOwner > 0 || theResult->groupName() == ModelAPI_ResultBody::group())
722     aDefaultName << "_" << anIndexInOwner + 1;
723   return std::pair<std::string, bool>(aDefaultName.str(), false);
724 }
725
726 std::set<FeaturePtr> getParents(const FeaturePtr& theFeature)
727 {
728   std::set<FeaturePtr> aParents;
729   for (FeaturePtr aCurFeat = theFeature; aCurFeat; ) {
730     CompositeFeaturePtr aFoundComposite;
731     const std::set<AttributePtr>& aRefs = aCurFeat->data()->refsToMe();
732     for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin();
733       anIt != aRefs.end(); ++anIt) {
734       FeaturePtr aF = ModelAPI_Feature::feature((*anIt)->owner());
735       aFoundComposite = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aF);
736       if (aFoundComposite && aFoundComposite->isSub(aCurFeat))
737         break;
738       else
739         aFoundComposite = CompositeFeaturePtr();
740     }
741
742     if (aFoundComposite) {
743       aParents.insert(aFoundComposite);
744       aCurFeat = aFoundComposite;
745     }
746     else {
747       // add the part containing high-level feature
748       SessionPtr aSession = ModelAPI_Session::get();
749       DocumentPtr aPartSetDoc = aSession->moduleDocument();
750       std::list<FeaturePtr> aPartSetFeatures = aPartSetDoc->allFeatures();
751       for (std::list<FeaturePtr>::const_iterator anIt = aPartSetFeatures.begin();
752         anIt != aPartSetFeatures.end(); ++anIt) {
753         aFoundComposite = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
754         if (aFoundComposite && aFoundComposite->isSub(aCurFeat)) {
755           aParents.insert(aFoundComposite);
756           break;
757         }
758       }
759
760       aCurFeat = FeaturePtr();
761     }
762   }
763   return aParents;
764 }
765
766 void removeResults(const std::list<ResultPtr>& theResults)
767 {
768   // collect all documents where the results must be removed
769   std::map<DocumentPtr, std::list<ResultPtr> > aDocs;
770
771   std::list<ResultPtr>::const_iterator aResIter = theResults.cbegin();
772   for(; aResIter != theResults.cend(); aResIter++) {
773     DocumentPtr aDoc = (*aResIter)->document();
774     if (!aDocs.count(aDoc))
775       aDocs[aDoc] = std::list<ResultPtr>();
776     aDocs[aDoc].push_back(*aResIter);
777   }
778   // create a "remove" feature in each doc
779   std::map<DocumentPtr, std::list<ResultPtr> >::iterator aDoc = aDocs.begin();
780   for(; aDoc != aDocs.end(); aDoc++) {
781     FeaturePtr aRemove = aDoc->first->addFeature("RemoveResults");
782     if (aRemove) {
783       for(aResIter = aDoc->second.cbegin(); aResIter != aDoc->second.cend(); aResIter++)
784         aRemove->selectionList("results")->append(*aResIter, GeomShapePtr());
785     }
786   }
787 }
788
789
790 //**************************************************************
791 void setDeflection(ResultPtr theResult, const double theDeflection)
792 {
793   if (!theResult.get())
794     return;
795
796   AttributeDoublePtr aDeflectionAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
797   if (aDeflectionAttr.get() != NULL) {
798     aDeflectionAttr->setValue(theDeflection);
799   }
800 }
801
802 // used by GUI only
803 // LCOV_EXCL_START
804 double getDeflection(const std::shared_ptr<ModelAPI_Result>& theResult)
805 {
806   double aDeflection = -1;
807   // get deflection from the attribute of the result
808   if (theResult.get() != NULL &&
809     theResult->data()->attribute(ModelAPI_Result::DEFLECTION_ID()).get() != NULL) {
810     AttributeDoublePtr aDoubleAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
811     if (aDoubleAttr.get() && aDoubleAttr->isInitialized()) {
812       double aValue = aDoubleAttr->value();
813       if (aValue > 0) /// zero value should not be used as a deflection(previous studies)
814         aDeflection = aDoubleAttr->value();
815     }
816   }
817   return aDeflection;
818 }
819
820 //******************************************************
821 void setColor(ResultPtr theResult, const std::vector<int>& theColor)
822 {
823   if (!theResult.get())
824     return;
825
826   AttributeIntArrayPtr aColorAttr = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
827   if (aColorAttr.get() != NULL) {
828     if (!aColorAttr->size()) {
829       aColorAttr->setSize(3);
830     }
831     aColorAttr->setValue(0, theColor[0]);
832     aColorAttr->setValue(1, theColor[1]);
833     aColorAttr->setValue(2, theColor[2]);
834   }
835 }
836
837 void getColor(const std::shared_ptr<ModelAPI_Result>& theResult, std::vector<int>& theColor)
838 {
839   theColor.clear();
840   // get color from the attribute of the result
841   if (theResult.get() != NULL &&
842     theResult->data()->attribute(ModelAPI_Result::COLOR_ID()).get() != NULL) {
843     AttributeIntArrayPtr aColorAttr = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
844     if (aColorAttr.get() && aColorAttr->size()) {
845       theColor.push_back(aColorAttr->value(0));
846       theColor.push_back(aColorAttr->value(1));
847       theColor.push_back(aColorAttr->value(2));
848     }
849   }
850 }
851
852 //******************************************************
853 void getIsoLines(const std::shared_ptr<ModelAPI_Result>& theResult, std::vector<int>& theNbLines)
854 {
855   theNbLines.clear();
856   if (theResult->groupName() == ModelAPI_ResultConstruction::group()) {
857     theNbLines.push_back(0);
858     theNbLines.push_back(0);
859   }
860   else {
861     // get color from the attribute of the result
862     if (theResult.get() != NULL &&
863       theResult->data()->attribute(ModelAPI_Result::ISO_LINES_ID()).get() != NULL) {
864       AttributeIntArrayPtr aAttr = theResult->data()->intArray(ModelAPI_Result::ISO_LINES_ID());
865       if (aAttr.get() && aAttr->size()) {
866         theNbLines.push_back(aAttr->value(0));
867         theNbLines.push_back(aAttr->value(1));
868       }
869     }
870   }
871 }
872
873 //******************************************************
874 void setIsoLines(ResultPtr theResult, const std::vector<int>& theIso)
875 {
876   if (!theResult.get())
877     return;
878
879   AttributeIntArrayPtr aAttr = theResult->data()->intArray(ModelAPI_Result::ISO_LINES_ID());
880   if (aAttr.get() != NULL) {
881     if (!aAttr->size()) {
882       aAttr->setSize(2);
883     }
884     aAttr->setValue(0, theIso[0]);
885     aAttr->setValue(1, theIso[1]);
886   }
887 }
888
889 //**************************************************************
890 void setTransparency(ResultPtr theResult, double theTransparency)
891 {
892   if (!theResult.get())
893     return;
894
895   AttributeDoublePtr anAttribute = theResult->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
896   if (anAttribute.get() != NULL) {
897     anAttribute->setValue(theTransparency);
898   }
899 }
900
901 double getTransparency(const std::shared_ptr<ModelAPI_Result>& theResult)
902 {
903   double aTransparency = -1;
904   // get transparency from the attribute of the result
905   if (theResult.get() != NULL &&
906     theResult->data()->attribute(ModelAPI_Result::TRANSPARENCY_ID()).get() != NULL) {
907     AttributeDoublePtr aDoubleAttr = theResult->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
908     if (aDoubleAttr.get() && aDoubleAttr->isInitialized()) {
909       aTransparency = aDoubleAttr->value();
910     }
911   }
912   return aTransparency;
913 }
914 // LCOV_EXCL_STOP
915
916 void copyVisualizationAttrs(
917   std::shared_ptr<ModelAPI_Result> theSource, std::shared_ptr<ModelAPI_Result> theDest)
918 {
919   // color
920   AttributeIntArrayPtr aSourceColor = theSource->data()->intArray(ModelAPI_Result::COLOR_ID());
921   if (aSourceColor.get() && aSourceColor->isInitialized() && aSourceColor->size()) {
922     AttributeIntArrayPtr aDestColor = theDest->data()->intArray(ModelAPI_Result::COLOR_ID());
923     if (aDestColor.get()) {
924       aDestColor->setSize(aSourceColor->size());
925       for(int a = 0; a < aSourceColor->size(); a++)
926         aDestColor->setValue(a, aSourceColor->value(a));
927     }
928   }
929   // Iso-lines
930   AttributeIntArrayPtr aSource = theSource->data()->intArray(ModelAPI_Result::ISO_LINES_ID());
931   if (aSource.get() && aSource->isInitialized() && aSource->size()) {
932     AttributeIntArrayPtr aDest = theDest->data()->intArray(ModelAPI_Result::ISO_LINES_ID());
933     if (aDest.get()) {
934       aDest->setSize(aSource->size());
935       for(int a = 0; a < aSource->size(); a++)
936         aDest->setValue(a, aSource->value(a));
937     }
938   }
939   // deflection
940   AttributeDoublePtr aSourceDefl = theSource->data()->real(ModelAPI_Result::DEFLECTION_ID());
941   if (aSourceDefl.get() && aSourceDefl->isInitialized()) {
942     AttributeDoublePtr aDestDefl = theDest->data()->real(ModelAPI_Result::DEFLECTION_ID());
943     if (aDestDefl.get()) {
944       aDestDefl->setValue(aSourceDefl->value());
945     }
946   }
947   // transparency
948   AttributeDoublePtr aSourceTransp = theSource->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
949   if (aSourceTransp.get() && aSourceTransp->isInitialized()) {
950     AttributeDoublePtr aDestTransp = theDest->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
951     if (aDestTransp.get()) {
952       aDestTransp->setValue(aSourceTransp->value());
953     }
954   }
955 }
956
957 } // namespace ModelAPI_Tools