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