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