Salome HOME
Fix issues related to selection of sub-shapes in a Part
[modules/shaper.git] / src / ModelHighAPI / ModelHighAPI_Dumper.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 "ModelHighAPI_Dumper.h"
22
23 #include <Config_PropManager.h>
24
25 #include <GeomAPI_Pnt.h>
26 #include <GeomAPI_Dir.h>
27 #include <GeomAPI_ShapeExplorer.h>
28
29 #include <GeomDataAPI_Dir.h>
30 #include <GeomDataAPI_Point.h>
31 #include <GeomDataAPI_Point2D.h>
32
33 #include <ModelAPI_AttributeBoolean.h>
34 #include <ModelAPI_AttributeDouble.h>
35 #include <ModelAPI_AttributeIntArray.h>
36 #include <ModelAPI_AttributeInteger.h>
37 #include <ModelAPI_AttributeRefAttr.h>
38 #include <ModelAPI_AttributeRefAttrList.h>
39 #include <ModelAPI_AttributeReference.h>
40 #include <ModelAPI_AttributeRefList.h>
41 #include <ModelAPI_AttributeSelection.h>
42 #include <ModelAPI_AttributeSelectionList.h>
43 #include <ModelAPI_AttributeString.h>
44 #include <ModelAPI_AttributeStringArray.h>
45 #include <ModelAPI_CompositeFeature.h>
46 #include <ModelAPI_Document.h>
47 #include <ModelAPI_Entity.h>
48 #include <ModelAPI_Feature.h>
49 #include <ModelAPI_Folder.h>
50 #include <ModelAPI_Result.h>
51 #include <ModelAPI_ResultBody.h>
52 #include <ModelAPI_ResultConstruction.h>
53 #include <ModelAPI_ResultPart.h>
54 #include <ModelAPI_Session.h>
55 #include <ModelAPI_Tools.h>
56
57 #include <ModelGeomAlgo_Shape.h>
58
59 #include <PartSetPlugin_Part.h>
60
61 #include <OSD_OpenFile.hxx>
62
63 #include <fstream>
64
65 static int gCompositeStackDepth = 0;
66
67 ModelHighAPI_Dumper* ModelHighAPI_Dumper::mySelf = 0;
68
69 ModelHighAPI_Dumper::ModelHighAPI_Dumper()
70   : myGeometricalSelection(false)
71 {
72   clear();
73 }
74
75 void ModelHighAPI_Dumper::setInstance(ModelHighAPI_Dumper* theDumper)
76 {
77   if (mySelf == 0)
78     mySelf = theDumper;
79 }
80
81 ModelHighAPI_Dumper* ModelHighAPI_Dumper::getInstance()
82 {
83   return mySelf;
84 }
85
86 void ModelHighAPI_Dumper::clear(bool bufferOnly)
87 {
88   myDumpBuffer.str("");
89   myDumpBuffer << std::setprecision(16);
90
91   clearNotDumped();
92
93   if (!bufferOnly) {
94     myFullDump.str("");
95     myFullDump << std::setprecision(16);
96
97     myNames.clear();
98     myModules.clear();
99     myFeatureCount.clear();
100     while (!myEntitiesStack.empty())
101       myEntitiesStack.pop();
102
103     myPostponed.clear();
104     myDumpPostponedInProgress = false;
105   }
106 }
107
108 void ModelHighAPI_Dumper::clearNotDumped()
109 {
110   myNotDumpedEntities.clear();
111 }
112
113 // Convert string to integer. If the string is not a number, return -1
114 static int toInt(const std::string& theString)
115 {
116   std::string::const_iterator aChar = theString.begin();
117   for (; aChar != theString.end(); ++aChar)
118     if (!std::isdigit(*aChar))
119       break;
120   if (aChar != theString.end())
121     return -1; // not a number
122   return std::stoi(theString);
123 }
124
125 const std::string& ModelHighAPI_Dumper::name(const EntityPtr& theEntity,
126                                              bool theSaveNotDumped,
127                                              bool theUseEntityName)
128 {
129   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
130   if (aFound != myNames.end())
131     return aFound->second.myCurrentName;
132
133   // entity is not found, store it
134   std::string aName, aKind;
135   bool isDefaultName = false;
136   bool isSaveNotDumped = theSaveNotDumped;
137   std::ostringstream aDefaultName;
138   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
139   if (aFeature) {
140     aName = aFeature->name();
141     aKind = aFeature->getKind();
142   } else {
143     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(theEntity);
144     if (aFolder) {
145       aName = aFolder->data()->name();
146       aKind = ModelAPI_Folder::ID();
147       isSaveNotDumped = false;
148     }
149   }
150
151   ObjectPtr anObject = std::dynamic_pointer_cast<ModelAPI_Object>(theEntity);
152   if (anObject) {
153     DocumentPtr aDoc = anObject->document();
154     std::pair<int, int>& aNbFeatures = myFeatureCount[aDoc][aKind];
155     aNbFeatures.first += 1;
156
157     size_t anIndex = aName.find(aKind);
158     if (anIndex == 0 && aName[aKind.length()] == '_') { // name starts with "FeatureKind_"
159       std::string anIdStr = aName.substr(aKind.length() + 1);
160       int anId = toInt(anIdStr);
161
162       // Check number of already registered objects of such kind. Index of current object
163       // should be the same to identify feature's name as automatically generated.
164       if (aNbFeatures.first == anId && aNbFeatures.second < anId) {
165         // name is not user-defined
166         isDefaultName = true;
167       }
168
169       if (anId > aNbFeatures.second)
170         aNbFeatures.second = anId;
171     }
172
173     // obtain default name for the feature
174     if (theUseEntityName)
175       aDefaultName << aName;
176     else {
177       int aFullIndex = 0;
178       NbFeaturesMap::const_iterator aFIt = myFeatureCount.begin();
179       for (; aFIt != myFeatureCount.end(); ++aFIt) {
180         std::map<std::string, std::pair<int, int> >::const_iterator aFound =
181           aFIt->second.find(aKind);
182         if (aFound != aFIt->second.end())
183           aFullIndex += aFound->second.first;
184       }
185       aDefaultName << aKind << "_" << aFullIndex;
186     }
187   }
188
189   myNames[theEntity] = EntityName(aDefaultName.str(), aName, isDefaultName);
190   if (isSaveNotDumped)
191     myNotDumpedEntities.insert(theEntity);
192
193   // store names of results
194   if (aFeature)
195     saveResultNames(aFeature);
196
197   return myNames[theEntity].myCurrentName;
198 }
199
200 const std::string& ModelHighAPI_Dumper::parentName(const FeaturePtr& theEntity)
201 {
202   const std::set<AttributePtr>& aRefs = theEntity->data()->refsToMe();
203   std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
204   for (; aRefIt != aRefs.end(); ++aRefIt) {
205     CompositeFeaturePtr anOwner = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(
206         ModelAPI_Feature::feature((*aRefIt)->owner()));
207     if (anOwner)
208       return name(anOwner);
209   }
210
211   static const std::string DUMMY;
212   return DUMMY;
213 }
214
215 void ModelHighAPI_Dumper::saveResultNames(const FeaturePtr& theFeature)
216 {
217   // Default name of the feature
218   bool isFeatureDefaultName = myNames[theFeature].myIsDefault;
219
220   // Save only names of results which is not correspond to default feature name
221   const std::list<ResultPtr>& aResults = theFeature->results();
222   std::list<ResultPtr> allRes;
223   ModelAPI_Tools::allResults(theFeature, allRes);
224   for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
225     std::pair<std::string, bool> aName = ModelAPI_Tools::getDefaultName(*aRes);
226     std::string aDefaultName = aName.first;
227     std::string aResName = (*aRes)->data()->name();
228     bool isUserDefined = !(isFeatureDefaultName && aDefaultName == aResName);
229     myNames[*aRes] =
230       EntityName(aResName, (isUserDefined ? aResName : std::string()), !isUserDefined);
231   }
232 }
233
234 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc,
235                                   const std::string& theFileName)
236 {
237   // dump top level document feature
238   static const std::string aDocName("partSet");
239   myNames[theDoc] = EntityName(aDocName, std::string(), true);
240   *this << aDocName << " = model.moduleDocument()" << std::endl;
241
242   // dump subfeatures and store result to file
243   return process(theDoc) && exportTo(theFileName);
244 }
245
246 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_Document>& theDoc)
247 {
248   bool isOk = true;
249   std::list<ObjectPtr> anObjects = theDoc->allObjects();
250   std::list<ObjectPtr>::const_iterator anObjIt = anObjects.begin();
251   // firstly, dump all parameters
252   for (; anObjIt != anObjects.end(); ++ anObjIt) {
253     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
254     if (aFeature)
255       dumpParameter(aFeature);
256   }
257   // dump all other features
258   for (anObjIt = anObjects.begin(); anObjIt != anObjects.end(); ++anObjIt) {
259     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anObjIt);
260     if (aCompFeat) // iteratively process composite features
261       isOk = process(aCompFeat) && isOk;
262     else if (!isDumped(EntityPtr(*anObjIt))) {
263       // dump folder
264       FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anObjIt);
265       if (aFolder)
266         dumpFolder(aFolder);
267       else {
268         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIt);
269         if (aFeature) // dump common feature
270           dumpFeature(aFeature);
271       }
272     }
273   }
274   // dump folders if any
275   dumpPostponed(true);
276   return isOk;
277 }
278
279 bool ModelHighAPI_Dumper::process(const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
280                                   bool isForce)
281 {
282   // increase composite features stack
283   ++gCompositeStackDepth;
284   // dump composite itself
285   if (!isDumped(EntityPtr(theComposite)) || isForce)
286     dumpFeature(FeaturePtr(theComposite), isForce);
287
288   // sub-part is processed independently, because it provides separate document
289   if (theComposite->getKind() == PartSetPlugin_Part::ID()) {
290     // dump name of the part if it is different from default
291     if (!myEntitiesStack.empty())
292       dumpEntitySetName();
293
294     // decrease composite features stack because we run into separate document
295     --gCompositeStackDepth;
296
297     ResultPartPtr aPartResult =
298         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theComposite->lastResult());
299     if (!aPartResult)
300       return false;
301     DocumentPtr aSubDoc = aPartResult->partDoc();
302     if (!aSubDoc)
303       return false;
304     // set name of document
305     const std::string& aPartName = myNames[theComposite].myCurrentName;
306     std::string aDocName = aPartName + "_doc";
307     myNames[aSubDoc] = EntityName(aDocName, std::string(), true);
308
309     // dump document in a separate line
310     *this << aDocName << " = " << aPartName << ".document()" << std::endl;
311     // dump features in the document
312     bool aRes = process(aSubDoc);
313     *this << "model.do()" << std::endl;
314     return aRes;
315   }
316
317   // dump sub-features
318   bool isOk = processSubs(theComposite);
319   // decrease composite features stack
320   --gCompositeStackDepth;
321
322   return isOk;
323 }
324
325 bool ModelHighAPI_Dumper::processSubs(
326   const std::shared_ptr<ModelAPI_CompositeFeature>& theComposite,
327   bool theDumpModelDo)
328 {
329   bool isOk = true;
330   // dump all sub-features;
331   bool isSubDumped = false;
332   int aNbSubs = theComposite->numberOfSubs();
333   for (int anIndex = 0; anIndex < aNbSubs; ++anIndex) {
334     FeaturePtr aFeature = theComposite->subFeature(anIndex);
335     if (isDumped(EntityPtr(aFeature)))
336       continue;
337
338     isSubDumped = true;
339     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aFeature);
340     if (aCompFeat) // iteratively process composite features
341       isOk = process(aCompFeat) && isOk;
342     else
343       dumpFeature(aFeature, true);
344   }
345
346   bool isDumpSetName = !myEntitiesStack.empty() &&
347       myEntitiesStack.top().myEntity == EntityPtr(theComposite);
348   bool isForceModelDo = isSubDumped && isDumpSetName &&
349       (myEntitiesStack.top().myUserName || !myEntitiesStack.top().myResults.empty());
350   // It is necessary for the sketch to create its result when complete (command "model.do()").
351   // This option is set by flat theDumpModelDo.
352   // However, nested sketches are rebuilt by parent feature, so, they do not need
353   // explicit call of "model.do()". This will be controlled by the depth of the stack.
354   if (isForceModelDo || (theDumpModelDo && gCompositeStackDepth <= 1))
355     *this << "model.do()" << std::endl;
356
357   // dump "setName" for composite feature
358   if (isDumpSetName)
359     dumpEntitySetName();
360   return isOk;
361 }
362
363 void ModelHighAPI_Dumper::postpone(const EntityPtr& theEntity)
364 {
365   // keep the name
366   name(theEntity, false);
367   myPostponed.push_back(theEntity);
368 }
369
370 void ModelHighAPI_Dumper::dumpPostponed(bool theDumpFolders)
371 {
372   if (myDumpPostponedInProgress)
373     return;
374
375   myDumpPostponedInProgress = true;
376   // make a copy of postponed entities, because the list will be updated
377   // if some features are not able to be dumped
378   std::list<EntityPtr> aPostponedCopy = myPostponed;
379   myPostponed.clear();
380
381   // iterate over postponed entities and try to dump them
382   std::list<EntityPtr>::const_iterator anIt = aPostponedCopy.begin();
383   for (; anIt != aPostponedCopy.end(); ++anIt) {
384     FolderPtr aFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(*anIt);
385     if (aFolder) {
386       if (theDumpFolders)
387         dumpFolder(aFolder);
388       else
389         myPostponed.push_back(*anIt);
390     }
391     else {
392       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
393       if (aFeature)
394         dumpFeature(aFeature, true);
395     }
396   }
397   myDumpPostponedInProgress = false;
398 }
399
400 void ModelHighAPI_Dumper::dumpSubFeatureNameAndColor(const std::string theSubFeatureGet,
401                                                      const FeaturePtr& theSubFeature)
402 {
403   name(theSubFeature, false);
404   myNames[theSubFeature] = EntityName(theSubFeatureGet, theSubFeature->name(), false);
405
406   // store results if they have user-defined names or colors
407   std::list<ResultPtr> aResultsWithNameOrColor;
408   const std::list<ResultPtr>& aResults = theSubFeature->results();
409   std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
410   for (; aResIt != aResults.end(); ++aResIt) {
411     std::string aResName = (*aResIt)->data()->name();
412     myNames[*aResIt] = EntityName(aResName, aResName, false);
413     aResultsWithNameOrColor.push_back(*aResIt);
414   }
415
416   // store just dumped entity to stack
417   myEntitiesStack.push(LastDumpedEntity(theSubFeature, true, aResultsWithNameOrColor));
418
419   dumpEntitySetName();
420 }
421
422 bool ModelHighAPI_Dumper::exportTo(const std::string& theFileName)
423 {
424   std::ofstream aFile;
425   OSD_OpenStream(aFile, theFileName.c_str(), std::ofstream::out);
426   if (!aFile.is_open())
427     return false;
428
429   // standard header (encoding + imported modules)
430   aFile << "# -*- coding: utf-8 -*-" << std::endl << std::endl;
431   for (ModulesMap::const_iterator aModIt = myModules.begin();
432        aModIt != myModules.end(); ++aModIt) {
433     aFile << "from " << aModIt->first << " import ";
434     if (aModIt->second.empty() ||
435         aModIt->second.find(std::string()) != aModIt->second.end())
436       aFile << "*"; // import whole module
437     else {
438       // import specific features
439       std::set<std::string>::const_iterator anObjIt = aModIt->second.begin();
440       aFile << *anObjIt;
441       for (++anObjIt; anObjIt != aModIt->second.end(); ++anObjIt)
442         aFile << ", " << *anObjIt;
443     }
444     aFile << std::endl;
445   }
446   if (!myModules.empty())
447     aFile << std::endl;
448
449   aFile << "from salome.shaper import model" << std::endl << std::endl;
450   aFile << "model.begin()" << std::endl;
451
452   // dump collected data
453   aFile << myFullDump.str();
454   aFile << myDumpBuffer.str();
455
456   // standard footer
457   aFile << "model.end()" << std::endl;
458
459   aFile.close();
460   clear();
461
462   return true;
463 }
464
465 void ModelHighAPI_Dumper::importModule(const std::string& theModuleName,
466                                        const std::string& theObject)
467 {
468   myModules[theModuleName].insert(theObject);
469 }
470
471 void ModelHighAPI_Dumper::dumpEntitySetName()
472 {
473   const LastDumpedEntity& aLastDumped = myEntitiesStack.top();
474   bool isBufferEmpty = myDumpBuffer.str().empty();
475
476   // dump "setName" for the entity
477   if (aLastDumped.myUserName) {
478     EntityName& anEntityNames = myNames[aLastDumped.myEntity];
479     if (!anEntityNames.myIsDefault)
480       myDumpBuffer << anEntityNames.myCurrentName << ".setName(\""
481                    << anEntityNames.myUserName << "\")" << std::endl;
482     // don't dump "setName" for the entity twice
483     anEntityNames.myUserName.clear();
484     anEntityNames.myIsDefault = true;
485   }
486   // dump "setName" for results
487   std::list<ResultPtr>::const_iterator aResIt = aLastDumped.myResults.begin();
488   std::list<ResultPtr>::const_iterator aResEnd = aLastDumped.myResults.end();
489   for (; aResIt != aResEnd; ++aResIt) {
490     // set result name
491     EntityName& anEntityNames = myNames[*aResIt];
492     if (!anEntityNames.myIsDefault) {
493       *this << *aResIt;
494       myDumpBuffer << ".setName(\"" << anEntityNames.myUserName << "\")" << std::endl;
495       // don't dump "setName" for the entity twice
496       anEntityNames.myUserName.clear();
497       anEntityNames.myIsDefault = true;
498     }
499     // set result color
500     if (!isDefaultColor(*aResIt)) {
501       AttributeIntArrayPtr aColor = (*aResIt)->data()->intArray(ModelAPI_Result::COLOR_ID());
502       if (aColor && aColor->isInitialized()) {
503         *this << *aResIt;
504         myDumpBuffer << ".setColor(" << aColor->value(0) << ", " << aColor->value(1)
505                      << ", " << aColor->value(2) << ")" << std::endl;
506       }
507     }
508     // set result deflection
509     if (!isDefaultDeflection(*aResIt)) {
510       AttributeDoublePtr aDeflectionAttr =
511         (*aResIt)->data()->real(ModelAPI_Result::DEFLECTION_ID());
512       if(aDeflectionAttr.get() && aDeflectionAttr->isInitialized()) {
513         *this << *aResIt;
514         myDumpBuffer << ".setDeflection(" << aDeflectionAttr->value() << ")" << std::endl;
515       }
516     }
517     // set result transparency
518     if (!isDefaultTransparency(*aResIt)) {
519       AttributeDoublePtr aTransparencyAttr =
520         (*aResIt)->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
521       if(aTransparencyAttr.get() && aTransparencyAttr->isInitialized()) {
522         *this << *aResIt;
523         myDumpBuffer << ".setTransparency(" << aTransparencyAttr->value() << ")" << std::endl;
524       }
525     }
526   }
527
528   myNames[aLastDumped.myEntity].myIsDumped = true;
529   myEntitiesStack.pop();
530
531   // clean buffer if it was clear before
532   if (isBufferEmpty) {
533     myFullDump << myDumpBuffer.str();
534     myDumpBuffer.str("");
535   }
536 }
537
538 bool ModelHighAPI_Dumper::isDumped(const EntityPtr& theEntity) const
539 {
540   EntityNameMap::const_iterator aFound = myNames.find(theEntity);
541   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theEntity);
542   return (aFound != myNames.end() && aFound->second.myIsDumped) ||
543          myFeaturesToSkip.find(aFeature) != myFeaturesToSkip.end();
544 }
545
546 bool ModelHighAPI_Dumper::isDumped(const AttributeRefAttrPtr& theRefAttr) const
547 {
548   FeaturePtr aFeature;
549   if (theRefAttr->isObject())
550     aFeature = ModelAPI_Feature::feature(theRefAttr->object());
551   else
552     aFeature = ModelAPI_Feature::feature(theRefAttr->attr()->owner());
553   return aFeature && isDumped(EntityPtr(aFeature));
554 }
555
556 bool ModelHighAPI_Dumper::isDumped(const AttributeRefListPtr& theRefList) const
557 {
558   std::list<ObjectPtr> aRefs = theRefList->list();
559   std::list<ObjectPtr>::iterator anIt = aRefs.begin();
560   for (; anIt != aRefs.end(); ++anIt) {
561     FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
562     if (aFeature && !isDumped(EntityPtr(aFeature)))
563       return false;
564   }
565   return true;
566 }
567
568 bool ModelHighAPI_Dumper::isDefaultColor(const ResultPtr& theResult) const
569 {
570   AttributeIntArrayPtr aColor = theResult->data()->intArray(ModelAPI_Result::COLOR_ID());
571   if (!aColor || !aColor->isInitialized())
572     return true;
573
574   std::string aSection, aName, aDefault;
575   theResult->colorConfigInfo(aSection, aName, aDefault);
576
577   // dump current color
578   std::ostringstream aColorInfo;
579   aColorInfo << aColor->value(0) << "," << aColor->value(1) << "," << aColor->value(2);
580
581   return aDefault == aColorInfo.str();
582 }
583
584 bool ModelHighAPI_Dumper::isDefaultDeflection(const ResultPtr& theResult) const
585 {
586   AttributeDoublePtr aDeflectionAttr = theResult->data()->real(ModelAPI_Result::DEFLECTION_ID());
587   if(!aDeflectionAttr || !aDeflectionAttr->isInitialized()) {
588     return true;
589   }
590
591   double aCurrent = aDeflectionAttr->value();
592   double aDefault = -1;
593
594   bool isConstruction = false;
595   std::string aResultGroup = theResult->groupName();
596   if (aResultGroup == ModelAPI_ResultConstruction::group())
597     isConstruction = true;
598   else if (aResultGroup == ModelAPI_ResultBody::group()) {
599     GeomShapePtr aGeomShape = theResult->shape();
600     if (aGeomShape.get()) {
601       // if the shape could not be exploded on faces, it contains only wires, edges, and vertices
602       // correction of deviation for them should not influence to the application performance
603       GeomAPI_ShapeExplorer anExp(aGeomShape, GeomAPI_Shape::FACE);
604       isConstruction = !anExp.more();
605     }
606   }
607   if (isConstruction)
608     aDefault = Config_PropManager::real("Visualization", "construction_deflection");
609   else
610     aDefault = Config_PropManager::real("Visualization", "body_deflection");
611
612   return fabs(aCurrent - aDefault) < 1.e-12;
613 }
614
615 bool ModelHighAPI_Dumper::isDefaultTransparency(const ResultPtr& theResult) const
616 {
617   AttributeDoublePtr anAttribute = theResult->data()->real(ModelAPI_Result::TRANSPARENCY_ID());
618   if(!anAttribute || !anAttribute->isInitialized()) {
619     return true;
620   }
621   return fabs(anAttribute->value()) < 1.e-12;
622 }
623
624 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
625 {
626   myDumpBuffer << theChar;
627   return *this;
628 }
629
630 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char* theString)
631 {
632   myDumpBuffer << theString;
633   return *this;
634 }
635
636 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::string& theString)
637 {
638   myDumpBuffer << theString;
639   return *this;
640 }
641
642 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const bool theValue)
643 {
644   myDumpBuffer << (theValue ? "True" : "False");
645   return *this;
646 }
647
648 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const int theValue)
649 {
650   myDumpBuffer << theValue;
651   return *this;
652 }
653
654 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const double theValue)
655 {
656   myDumpBuffer << theValue;
657   return *this;
658 }
659
660 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Pnt>& thePoint)
661 {
662   importModule("GeomAPI", "GeomAPI_Pnt");
663   myDumpBuffer << "GeomAPI_Pnt(" << thePoint->x() << ", "
664                << thePoint->y() << ", " << thePoint->z() << ")";
665   return *this;
666 }
667
668 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const std::shared_ptr<GeomAPI_Dir>& theDir)
669 {
670   importModule("GeomAPI", "GeomAPI_Dir");
671   myDumpBuffer << "GeomAPI_Dir(" << theDir->x() << ", "
672                << theDir->y() << ", " << theDir->z() << ")";
673   return *this;
674 }
675
676 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
677     const std::shared_ptr<GeomDataAPI_Dir>& theDir)
678 {
679   myDumpBuffer << theDir->x() << ", " << theDir->y() << ", " << theDir->z();
680   return *this;
681 }
682
683 static void dumpArray(std::ostringstream& theOutput, int theSize,
684                       double* theValues, std::string* theTexts)
685 {
686   for (int i = 0; i < theSize; ++i) {
687     if (i > 0)
688       theOutput << ", ";
689     if (theTexts[i].empty())
690       theOutput << theValues[i];
691     else
692       theOutput << "\"" << theTexts[i] << "\"";
693   }
694 }
695
696 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
697     const std::shared_ptr<GeomDataAPI_Point>& thePoint)
698 {
699   static const int aSize = 3;
700   double aValues[aSize] = {thePoint->x(), thePoint->y(), thePoint->z()};
701   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY(), thePoint->textZ()};
702   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
703   return *this;
704 }
705
706 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
707     const std::shared_ptr<GeomDataAPI_Point2D>& thePoint)
708 {
709   static const int aSize = 2;
710   double aValues[aSize] = {thePoint->x(), thePoint->y()};
711   std::string aTexts[aSize] = {thePoint->textX(), thePoint->textY()};
712   dumpArray(myDumpBuffer, aSize, aValues, aTexts);
713   return *this;
714 }
715
716 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
717     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
718 {
719   myDumpBuffer << (theAttrBool->value() ? "True" : "False");
720   return *this;
721 }
722
723 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
724     const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt)
725 {
726   std::string aText = theAttrInt->text();
727   if (aText.empty())
728     myDumpBuffer << theAttrInt->value();
729   else
730     myDumpBuffer << "\"" << aText << "\"";
731   return *this;
732 }
733
734 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
735     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
736 {
737   std::string aText = theAttrReal->text();
738   if (aText.empty())
739     myDumpBuffer << theAttrReal->value();
740   else
741     myDumpBuffer << "\"" << aText << "\"";
742   return *this;
743 }
744
745 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
746     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
747 {
748   myDumpBuffer << "\"" << theAttrStr->value() << "\"";
749   return *this;
750 }
751
752 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FolderPtr& theFolder)
753 {
754   myDumpBuffer << name(theFolder);
755
756   // add dumped folder to a stack
757   if (!myNames[theFolder].myIsDumped &&
758      (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theFolder))
759     myEntitiesStack.push(LastDumpedEntity(theFolder, !myNames[theFolder].myIsDefault));
760
761   return *this;
762 }
763
764 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const FeaturePtr& theEntity)
765 {
766   myDumpBuffer << name(theEntity);
767
768   if (!myNames[theEntity].myIsDumped) {
769     bool isUserDefinedName = !myNames[theEntity].myIsDefault;
770     // store results if they have user-defined names or colors
771     std::list<ResultPtr> aResultsWithNameOrColor;
772     std::list<ResultPtr> allRes;
773     ModelAPI_Tools::allResults(theEntity, allRes);
774     for(std::list<ResultPtr>::iterator aRes = allRes.begin(); aRes != allRes.end(); aRes++) {
775       if(!myNames[*aRes].myIsDefault || !isDefaultColor(*aRes) ||
776          !isDefaultDeflection(*aRes) || !isDefaultTransparency(*aRes))
777         aResultsWithNameOrColor.push_back(*aRes);
778     }
779     // store just dumped entity to stack
780     if (myEntitiesStack.empty() || myEntitiesStack.top().myEntity != theEntity)
781       myEntitiesStack.push(
782           LastDumpedEntity(theEntity, isUserDefinedName, aResultsWithNameOrColor));
783   }
784
785   // remove entity from the list of not dumped items
786   myNotDumpedEntities.erase(theEntity);
787   return *this;
788 }
789
790 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ResultPtr& theResult)
791 {
792   // iterate in the structure of sub-results to the parent
793   ResultPtr aCurRes = theResult;
794   FeaturePtr aFeature = ModelAPI_Feature::feature(theResult);
795   std::list<int> anIndices; // indexes of results in the parent result, starting from topmost
796   while(aCurRes.get()) {
797     ResultBodyPtr aParent = ModelAPI_Tools::bodyOwner(aCurRes);
798     if (aParent) {
799       anIndices.push_front(ModelAPI_Tools::bodyIndex(aCurRes));
800     } else { // index of the result in the feature
801       std::list<ResultPtr>::const_iterator aRes = aFeature->results().cbegin();
802       for(int anIndex = 0; aRes != aFeature->results().cend(); aRes++, anIndex++) {
803         if (*aRes == aCurRes) {
804           anIndices.push_front(anIndex);
805           break;
806         }
807       }
808     }
809     aCurRes = aParent;
810   }
811
812   myDumpBuffer << name(aFeature);
813   for (std::list<int>::iterator anI = anIndices.begin(); anI != anIndices.end(); anI++) {
814     if (anI == anIndices.begin()) {
815       if(*anI == 0) {
816         myDumpBuffer << ".result()";
817       }
818       else {
819         myDumpBuffer << ".results()[" << *anI << "]";
820       }
821     } else {
822       myDumpBuffer << ".subResult(" << *anI << ")";
823     }
824   }
825
826   return *this;
827 }
828
829 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const ObjectPtr& theObject)
830 {
831   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
832   if(aFeature.get()) {
833     myDumpBuffer << name(aFeature);
834     return *this;
835   }
836
837   ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
838   if(aResult.get()) {
839     *this << aResult;
840     return *this;
841   }
842
843   return *this;
844 }
845
846 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const AttributePtr& theAttr)
847 {
848   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttr->owner());
849
850   std::string aWrapperPrefix, aWrapperSuffix;
851   // Check the attribute belongs to copied (in multi-translation or multi-rotation) feature.
852   // In this case we need to cast explicitly feature to appropriate type.
853   AttributeBooleanPtr isCopy = anOwner->boolean("Copy");
854   if (isCopy.get() && isCopy->value()) {
855     aWrapperPrefix = featureWrapper(anOwner) + "(";
856     aWrapperSuffix = ")";
857     importModule("SketchAPI");
858   }
859
860   myDumpBuffer << aWrapperPrefix << name(anOwner) << aWrapperSuffix
861                << "." << attributeGetter(anOwner, theAttr->id()) << "()";
862   return *this;
863 }
864
865 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
866     const std::shared_ptr<ModelAPI_AttributeRefAttr>& theRefAttr)
867 {
868   if (theRefAttr->isObject())
869     *this << theRefAttr->object();
870   else
871     *this << theRefAttr->attr();
872   return *this;
873 }
874
875 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
876     const std::shared_ptr<ModelAPI_AttributeRefAttrList>& theRefAttrList)
877 {
878   myDumpBuffer << "[";
879   std::list<std::pair<ObjectPtr, AttributePtr> > aList = theRefAttrList->list();
880   bool isAdded = false;
881   std::list<std::pair<ObjectPtr, AttributePtr> >::const_iterator anIt = aList.begin();
882   for (; anIt != aList.end(); ++anIt) {
883     if (isAdded)
884       myDumpBuffer << ", ";
885     else
886       isAdded = true;
887     if (anIt->first)
888       *this << anIt->first;
889     else if (anIt->second)
890       * this << anIt->second;
891   }
892   myDumpBuffer << "]";
893   return *this;
894 }
895
896 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
897     const std::shared_ptr<ModelAPI_AttributeReference>& theReference)
898 {
899   *this << theReference->value();
900   return *this;
901 }
902
903 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
904     const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList)
905 {
906   static const int aThreshold = 2;
907   // if number of elements in the list if greater than a threshold,
908   // dump it in a separate line with specific name
909   std::string aDumped = myDumpBuffer.str();
910   if (aDumped.empty() || theRefList->size() <= aThreshold) {
911     myDumpBuffer << "[";
912     std::list<ObjectPtr> aList = theRefList->list();
913     bool isAdded = false;
914     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
915     for (; anIt != aList.end(); ++anIt) {
916       if (isAdded)
917         myDumpBuffer << ", ";
918       else
919         isAdded = true;
920
921       *this << *anIt;
922     }
923     myDumpBuffer << "]";
924   } else {
925     // clear buffer and store list "as is"
926     myDumpBuffer.str("");
927     *this << theRefList;
928     // save buffer and clear it again
929     std::string aDumpedList = myDumpBuffer.str();
930     myDumpBuffer.str("");
931     // obtain name of list
932     FeaturePtr anOwner = ModelAPI_Feature::feature(theRefList->owner());
933     std::string aListName = name(anOwner) + "_objects";
934     // store all previous data
935     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
936                  << aDumped << aListName;
937   }
938   return *this;
939 }
940
941 static int possibleSelectionsByPoint(const GeomPointPtr& thePoint,
942                                      const ResultPtr& theResult,
943                                      const GeomShapePtr& theShape,
944                                      const FeaturePtr& theStartFeature,
945                                      const FeaturePtr& theEndFeature)
946 {
947   DocumentPtr aDoc1 = theStartFeature->document();
948   DocumentPtr aDoc2 = theEndFeature->document();
949
950   std::list<FeaturePtr> aFeatures = aDoc1->allFeatures();
951   if (aDoc1 != aDoc2) {
952     std::list<FeaturePtr> anAdditionalFeatures = aDoc2->allFeatures();
953     aFeatures.insert(aFeatures.end(), anAdditionalFeatures.begin(), anAdditionalFeatures.end());
954   }
955
956   CompositeFeaturePtr aLastCompositeFeature;
957
958   std::list<FeaturePtr>::const_iterator aFIt = aFeatures.begin();
959   while (aFIt != aFeatures.end() && *aFIt != theStartFeature) {
960     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
961     if (aCompFeat)
962       aLastCompositeFeature = aCompFeat;
963     ++aFIt;
964   }
965
966   // collect the list of composite features, containing the last feature;
967   // these features should be excluded from searching,
968   // because the feature cannot select sub-shapes from its parent
969   std::set<FeaturePtr> aEndFeatureParents = ModelAPI_Tools::getParents(theEndFeature);
970
971   int aNbPossibleSelections = 0;
972   for (; aFIt != aFeatures.end() && *aFIt != theEndFeature; ++aFIt) {
973     bool isSkipFeature = false;
974     if (aLastCompositeFeature && aLastCompositeFeature->isSub(*aFIt))
975       isSkipFeature = true;
976     CompositeFeaturePtr aCompFeat = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*aFIt);
977     if (aCompFeat) {
978       ResultPartPtr aPartRes =
979           std::dynamic_pointer_cast<ModelAPI_ResultPart>(aCompFeat->firstResult());
980       if (!aPartRes)
981         aLastCompositeFeature = aCompFeat;
982       if (aEndFeatureParents.find(aCompFeat) != aEndFeatureParents.end()) {
983         // do not process the parent for the last feature,
984         // because it cannot select objects from its parent
985         isSkipFeature = true;
986       }
987     }
988     if (isSkipFeature)
989       continue;
990
991     std::list<ModelGeomAlgo_Shape::SubshapeOfResult> anApproproate;
992     if (ModelGeomAlgo_Shape::findSubshapeByPoint(*aFIt, thePoint, theShape->shapeType(),
993                                                  anApproproate)) {
994       std::list<ModelGeomAlgo_Shape::SubshapeOfResult>::iterator anApIt = anApproproate.begin();
995       for (; anApIt != anApproproate.end(); ++anApIt) {
996         ++aNbPossibleSelections;
997
998         // stop if the target shape and result are found
999         GeomShapePtr aCurShape = anApIt->mySubshape;
1000         if (!aCurShape)
1001           aCurShape = anApIt->myResult->shape();
1002
1003         if (anApIt->myResult->isSame(theResult) && aCurShape->isSame(theShape))
1004           break;
1005       }
1006     }
1007   }
1008   return aNbPossibleSelections;
1009 }
1010
1011 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1012     const std::shared_ptr<ModelAPI_AttributeSelection>& theAttrSelect)
1013 {
1014   myDumpBuffer << "model.selection(";
1015
1016   if(!theAttrSelect->isInitialized()) {
1017     myDumpBuffer << ")";
1018     return *this;
1019   }
1020
1021   GeomShapePtr aShape = theAttrSelect->value();
1022   if(!aShape.get()) {
1023     aShape = theAttrSelect->context()->shape();
1024   }
1025
1026   if(!aShape.get()) {
1027     myDumpBuffer << ")";
1028     return *this;
1029   }
1030
1031   // how to dump selection: construction features are dumped by name always
1032   bool isDumpByGeom = myGeometricalSelection;
1033   FeaturePtr aSelectedFeature;
1034   if (isDumpByGeom) {
1035     ResultPtr aRes = theAttrSelect->context();
1036     if (aRes)
1037       aSelectedFeature = ModelAPI_Feature::feature(aRes->data()->owner());
1038     else
1039       aSelectedFeature = theAttrSelect->contextFeature();
1040
1041     if (aSelectedFeature)
1042       isDumpByGeom = aSelectedFeature->isInHistory();
1043   }
1044
1045   myDumpBuffer << "\"" << aShape->shapeTypeStr();
1046   if (isDumpByGeom) {
1047     // check the selected item is a ResultPart;
1048     // in this case it is necessary to get shape with full transformation
1049     // for correct calculation of the middle point
1050     ResultPartPtr aResPart =
1051         std::dynamic_pointer_cast<ModelAPI_ResultPart>(theAttrSelect->context());
1052     if (aResPart && aShape->shapeType() == GeomAPI_Shape::COMPOUND)
1053       aShape = aResPart->shape();
1054     GeomPointPtr aMiddlePoint = aShape->middlePoint();
1055     // calculate number of features, which could be selected by the same point
1056     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelect->owner());
1057     int aNbPossibleSelections = possibleSelectionsByPoint(aMiddlePoint,
1058         theAttrSelect->context(), aShape, aSelectedFeature, anOwner);
1059
1060     // produce the index if the number of applicable features is greater than 1
1061     std::string anIndex;
1062     if (aNbPossibleSelections > 1) {
1063       std::ostringstream anOutput;
1064       anOutput << "_" << aNbPossibleSelections;
1065       anIndex = anOutput.str();
1066     }
1067
1068     myDumpBuffer << anIndex << "\", ";
1069     *this << aMiddlePoint;
1070   }
1071   else
1072     myDumpBuffer << "\", \"" << theAttrSelect->namingName() << "\"";
1073   myDumpBuffer << ")";
1074   return *this;
1075 }
1076
1077 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1078     const std::shared_ptr<ModelAPI_AttributeSelectionList>& theAttrSelList)
1079 {
1080   static const int aThreshold = 2;
1081   // if number of elements in the list if greater than a threshold,
1082   // dump it in a separate line with specific name
1083   std::string aDumped = myDumpBuffer.str();
1084
1085   if (aDumped.empty() || theAttrSelList->size() <= aThreshold) {
1086     myDumpBuffer << "[";
1087
1088     GeomShapePtr aShape;
1089     std::string aShapeTypeStr;
1090
1091     bool isAdded = false;
1092
1093     for(int anIndex = 0; anIndex < theAttrSelList->size(); ++anIndex) {
1094       AttributeSelectionPtr anAttribute = theAttrSelList->value(anIndex);
1095       aShape = anAttribute->value();
1096       if(!aShape.get()) {
1097         ResultPtr aContext = anAttribute->context();
1098         if (aContext.get())
1099           aShape = aContext->shape();
1100       }
1101
1102       if(!aShape.get()) {
1103         continue;
1104       }
1105
1106       if(isAdded) {
1107         myDumpBuffer << ", ";
1108       } else {
1109         isAdded = true;
1110       }
1111       *this << anAttribute;
1112     }
1113
1114     myDumpBuffer << "]";
1115   } else {
1116     // clear buffer and store list "as is"
1117     myDumpBuffer.str("");
1118     *this << theAttrSelList;
1119     // save buffer and clear it again
1120     std::string aDumpedList = myDumpBuffer.str();
1121     myDumpBuffer.str("");
1122     // obtain name of list (the feature may contain several selection lists)
1123     FeaturePtr anOwner = ModelAPI_Feature::feature(theAttrSelList->owner());
1124     std::string aListName = name(anOwner) + "_objects";
1125     std::list<AttributePtr> aSelLists =
1126         anOwner->data()->attributes(ModelAPI_AttributeSelectionList::typeId());
1127     if (aSelLists.size() > 1) {
1128       int anIndex = 1;
1129       for (std::list<AttributePtr>::iterator aSIt = aSelLists.begin();
1130            aSIt != aSelLists.end(); ++aSIt, ++anIndex)
1131         if ((*aSIt).get() == theAttrSelList.get())
1132           break;
1133       std::ostringstream aSStream;
1134       aSStream << aListName << "_" << anIndex;
1135       aListName = aSStream.str();
1136     }
1137     // store all previous data
1138     myDumpBuffer << aListName << " = " << aDumpedList << std::endl
1139                  << aDumped << aListName;
1140   }
1141   return *this;
1142 }
1143
1144 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
1145   const std::shared_ptr<ModelAPI_AttributeStringArray>& theArray)
1146 {
1147   myDumpBuffer<<"[";
1148   for(int anIndex = 0; anIndex < theArray->size(); ++anIndex) {
1149     if (anIndex != 0)
1150       myDumpBuffer<<", ";
1151
1152     myDumpBuffer<<"\""<<theArray->value(anIndex)<<"\"";
1153   }
1154
1155   myDumpBuffer<<"]";
1156   return *this;
1157 }
1158
1159 /// Dump std::endl
1160 ModelHighAPI_Dumper& operator<<(ModelHighAPI_Dumper& theDumper,
1161                                 std::basic_ostream<char>& (*theEndl)(std::basic_ostream<char>&))
1162 {
1163   theDumper.myDumpBuffer << theEndl;
1164
1165   if (!theDumper.myEntitiesStack.empty()) {
1166     bool isCopy;
1167     // all copies have been stored into stack, pop them all
1168     do {
1169       isCopy = false;
1170       // Name for composite feature is dumped when all sub-entities are dumped
1171       // (see method ModelHighAPI_Dumper::processSubs).
1172       const ModelHighAPI_Dumper::LastDumpedEntity& aLastDumped = theDumper.myEntitiesStack.top();
1173       CompositeFeaturePtr aComposite =
1174           std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aLastDumped.myEntity);
1175       if (!aComposite) {
1176         theDumper.dumpEntitySetName();
1177         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aLastDumped.myEntity);
1178         if (aFeature) {
1179           AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1180           isCopy = aCopyAttr.get() && aCopyAttr->value();
1181         }
1182       }
1183     } while (isCopy && !theDumper.myEntitiesStack.empty());
1184   }
1185
1186   // store all not-dumped entities first
1187   std::set<EntityPtr> aNotDumped = theDumper.myNotDumpedEntities;
1188   std::string aBufCopy = theDumper.myDumpBuffer.str();
1189   theDumper.clear(true);
1190   std::set<EntityPtr>::const_iterator anIt = aNotDumped.begin();
1191   for (; anIt != aNotDumped.end(); ++anIt) {
1192     // if the feature is composite, dump it with all subs
1193     CompositeFeaturePtr aCompFeat =
1194         std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(*anIt);
1195     if (aCompFeat)
1196       theDumper.process(aCompFeat, true);
1197     else {
1198       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anIt);
1199       theDumper.dumpFeature(aFeature, true);
1200       // dump the Projection feature which produces this "Copy" entity
1201       AttributeBooleanPtr aCopyAttr = aFeature->boolean("Copy");
1202       if (aCopyAttr.get() && aCopyAttr->value())
1203       {
1204         const std::set<AttributePtr>& aRefs = aFeature->data()->refsToMe();
1205         std::set<AttributePtr>::iterator aRefIt = aRefs.begin();
1206         for (; aRefIt != aRefs.end(); ++aRefIt)
1207           if ((*aRefIt)->id() == "ProjectedFeature")
1208           { // process projection only
1209             FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
1210             if (anOwner && !theDumper.isDumped(EntityPtr(anOwner)))
1211               theDumper.dumpFeature(anOwner, true);
1212           }
1213       }
1214     }
1215   }
1216
1217   // avoid multiple empty lines
1218   size_t anInd = std::string::npos;
1219   while ((anInd = aBufCopy.find("\n\n\n")) != std::string::npos)
1220     aBufCopy.erase(anInd, 1);
1221   // then store currently dumped string
1222   theDumper.myFullDump << aBufCopy;
1223
1224   // now, store all postponed features
1225   theDumper.dumpPostponed();
1226
1227   return theDumper;
1228 }