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