Salome HOME
Issue #20274: No intersection point from python dump
authorArtem Zhidkov <Artem.Zhidkov@opencascade.com>
Thu, 3 Dec 2020 08:16:59 +0000 (11:16 +0300)
committerArtem Zhidkov <Artem.Zhidkov@opencascade.com>
Thu, 3 Dec 2020 08:16:59 +0000 (11:16 +0300)
Improve dumping of the following entities to provide correct update of the sketch elements they produce:
* Linear Copy
* Angular Copy
* Mirror

15 files changed:
src/ModelHighAPI/ModelHighAPI_Dumper.cpp
src/ModelHighAPI/ModelHighAPI_Dumper.h
src/ModelHighAPI/ModelHighAPI_Tools.cpp
src/SketchAPI/SketchAPI_Mirror.cpp
src/SketchAPI/SketchAPI_Mirror.h
src/SketchAPI/SketchAPI_Rotation.cpp
src/SketchAPI/SketchAPI_Rotation.h
src/SketchAPI/SketchAPI_Translation.cpp
src/SketchAPI/SketchAPI_Translation.h
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_ConstraintMirror.cpp
src/SketchPlugin/SketchPlugin_Offset.cpp
src/SketchPlugin/Test/Test20274_1.py [new file with mode: 0644]
src/SketchPlugin/Test/Test20274_2.py [new file with mode: 0644]
src/SketchPlugin/Test/Test20274_3.py [new file with mode: 0644]

index 9d2c5e5f0f96ca5eede7510c24473e650a8c86db..324b827d0357be534a31ffe7b9e144975296c157 100644 (file)
@@ -992,6 +992,18 @@ bool ModelHighAPI_Dumper::isDumped(const AttributeRefListPtr& theRefList) const
   return true;
 }
 
+size_t ModelHighAPI_Dumper::indexOfFirstNotDumped(
+    const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList) const
+{
+  size_t anIndex = 0;
+  std::list<ObjectPtr> anObjects = theRefList->list();
+  for (std::list<ObjectPtr>::const_iterator anIt = anObjects.begin();
+       anIt != anObjects.end(); ++anIt, ++anIndex)
+    if (!isDumped(ModelAPI_Feature::feature(*anIt)))
+      break;
+  return anIndex;
+}
+
 static bool isSketchSub(const FeaturePtr& theFeature)
 {
   static const std::string SKETCH("Sketch");
@@ -1071,7 +1083,7 @@ bool ModelHighAPI_Dumper::dumpCommentBeforeFeature(const FeaturePtr& theFeature)
   if (aFilters)
     return false;
   // all other features should be commented before the dump
-  return true;
+  return !isDumped(theFeature);
 }
 
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(const char theChar)
@@ -1445,6 +1457,9 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     bool isAdded = false;
     std::list<ObjectPtr>::const_iterator anIt = aList.begin();
     for (; anIt != aList.end(); ++anIt) {
+      if (!isDumped(ModelAPI_Feature::feature(*anIt)))
+        break; // stop if the object is not dumped yet (parent feature should be postponed)
+
       if (isAdded)
         *myDumpStorage << ", ";
       else
index 1fa21e9da80e7b181ed6aa4f6859b80f5fa4da40..8e12c05bbfa0720b0ec49431dd78994da6e627a0 100644 (file)
@@ -354,6 +354,10 @@ public:
   MODELHIGHAPI_EXPORT
   bool isDumped(const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList) const;
 
+  /// Returns the index of the first object in the list which is not dumped yet.
+  MODELHIGHAPI_EXPORT
+  size_t indexOfFirstNotDumped(const std::shared_ptr<ModelAPI_AttributeRefList>& theRefList) const;
+
   /// Export variables names to another module (calls exportVariable implemented in python)
   MODELHIGHAPI_EXPORT virtual void exportVariables() const;
 
index 0b72c418f795263e3808897a343f6f11068bf968..7f959291cb4d815416327ca4a4064d2235b31dec 100644 (file)
@@ -168,8 +168,18 @@ void fillAttribute(const std::shared_ptr<ModelAPI_Object> & theValue,
 void fillAttribute(const std::list<std::shared_ptr<ModelAPI_Object> > & theValue,
                    const std::shared_ptr<ModelAPI_AttributeRefList> & theAttribute)
 {
-  theAttribute->clear();
-  for (auto it = theValue.begin(); it != theValue.end(); ++it)
+  int aSize = theAttribute->size();
+  // keep objects at the beginning of the list if they the same
+  auto it = theValue.begin();
+  for (int anIndex = 0; it != theValue.end() && anIndex < aSize; ++it, ++anIndex)
+    if (theAttribute->object(anIndex) != *it) {
+      // remove the tail of the list
+      while (++anIndex <= aSize)
+        theAttribute->removeLast();
+      break;
+    }
+  // append the rest of elements
+  for (; it != theValue.end(); ++it)
     theAttribute->append(*it);
 }
 
index 1013e3cf5c7d2605fbb313132eca80fcb495e506..534f505768e58580d5570d9e8ec53c9f02e38026 100644 (file)
@@ -39,15 +39,18 @@ SketchAPI_Mirror::SketchAPI_Mirror(
 {
   if (initialize()) {
     fillAttribute(theMirrorLine, mirrorLine());
-    fillAttribute(theObjects, mirrorList());
-
-    execute();
+    setMirrorList(theObjects);
   }
 }
 
 SketchAPI_Mirror::~SketchAPI_Mirror()
 {
+}
 
+void SketchAPI_Mirror::setMirrorList(const std::list<std::shared_ptr<ModelAPI_Object> >& theObjects)
+{
+  fillAttribute(theObjects, mirrorList());
+  execute();
 }
 
 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_Mirror::mirrored() const
@@ -69,44 +72,71 @@ void SketchAPI_Mirror::dump(ModelHighAPI_Dumper& theDumper) const
   FeaturePtr aBase = feature();
   const std::string& aSketchName = theDumper.parentName(aBase);
 
-
   AttributeRefAttrPtr aMirrorLine = mirrorLine();
   AttributeRefListPtr aMirrorObjects = mirrorList();
 
   // Check all attributes are already dumped. If not, store the constraint as postponed.
-  if (!theDumper.isDumped(aMirrorLine) || !theDumper.isDumped(aMirrorObjects)) {
+  size_t aFirstNotDumped = theDumper.indexOfFirstNotDumped(aMirrorObjects);
+  if (!theDumper.isDumped(aMirrorLine) || aFirstNotDumped == 0) {
+    theDumper.postpone(aBase);
+    return;
+  }
+
+
+  // the number of dumped aMirrorObjects is not changed, no need to dump anything
+  static std::map<FeaturePtr, size_t> aNbDumpedArguments;
+  std::map<FeaturePtr, size_t>::iterator aFound = aNbDumpedArguments.find(aBase);
+  if (aFound != aNbDumpedArguments.end() && aFound->second == aFirstNotDumped) {
     theDumper.postpone(aBase);
     return;
   }
+  else
+    aNbDumpedArguments[aBase] = aFirstNotDumped;
 
-  theDumper << aBase << " = " << aSketchName << ".addMirror(" << aMirrorLine << ", "
-            << aMirrorObjects << ")" << std::endl;
+  if (theDumper.isDumped(aBase)) {
+    // the feature is already dumped, but it was postponed, because of some arguments
+    // were not dumped yet, thus, it is necessary to update the list of rotated objects
+    theDumper << "\n### Update " << aBase->getKind() << std::endl;
+    theDumper << aBase << ".setMirrorList(" << aMirrorObjects << ")" << std::endl;
+  }
+  else {
+    // the feature is not dumped yet, make the full dump
+    theDumper << aBase << " = " << aSketchName << ".addMirror(" << aMirrorLine << ", "
+              << aMirrorObjects << ")" << std::endl;
+  }
 
   // Dump variables for a list of mirrored features
   theDumper << "[";
   std::list<std::shared_ptr<SketchAPI_SketchEntity> > aList = mirrored();
   std::list<std::shared_ptr<SketchAPI_SketchEntity> >::const_iterator anIt = aList.begin();
-  for (; anIt != aList.end(); ++anIt) {
+  for (size_t anIndex = 0; anIndex < aFirstNotDumped; ++anIndex, ++anIt) {
     if (anIt != aList.begin())
       theDumper << ", ";
     theDumper << (*anIt)->feature();
   }
   theDumper << "] = " << theDumper.name(aBase) << ".mirrored()" << std::endl;
 
-  // Set necessary "auxiliary" flag for mirrored features
-  // (flag is set if it differs to base entity)
-  std::list<ObjectPtr> aMirList = aMirrorObjects->list();
-  std::list<ObjectPtr>::const_iterator aMIt = aMirList.begin();
-  for (anIt = aList.begin(); aMIt != aMirList.end(); ++aMIt, ++anIt) {
-    FeaturePtr aFeature = ModelAPI_Feature::feature(*aMIt);
-    if (!aFeature)
-      continue;
-    bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
+  if (theDumper.isDumped(aMirrorObjects)) {
+    aNbDumpedArguments.erase(aBase);
+    // Set necessary "auxiliary" flag for mirrored features
+    // (flag is set if it differs to base entity)
+    std::list<ObjectPtr> aMirList = aMirrorObjects->list();
+    std::list<ObjectPtr>::const_iterator aMIt = aMirList.begin();
+    for (anIt = aList.begin(); aMIt != aMirList.end(); ++aMIt, ++anIt) {
+      FeaturePtr aFeature = ModelAPI_Feature::feature(*aMIt);
+      if (!aFeature)
+        continue;
+      bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
 
-    aFeature = (*anIt)->feature();
-    bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
-    if (aFeatAux != aBaseAux)
-      theDumper << theDumper.name((*anIt)->feature(), false)
-                << ".setAuxiliary(" << aFeatAux << ")" <<std::endl;
+      aFeature = (*anIt)->feature();
+      bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
+      if (aFeatAux != aBaseAux)
+        theDumper << theDumper.name((*anIt)->feature(), false)
+                  << ".setAuxiliary(" << aFeatAux << ")" <<std::endl;
+    }
+  }
+  else {
+    // If all refereced objects are not dumped yet, mark the feature as postponed.
+    theDumper.postpone(aBase);
   }
 }
index 9c83bba308ecac28ea3f53adb2b205bc8b850c98..1530559fb89fdc8b1e60ec47b52bb739b1d6e52c 100644 (file)
@@ -64,6 +64,10 @@ public:
               ModelAPI_AttributeRefList, /** Mirrored objects */
   )
 
+  /// Set list of original objects
+  SKETCHAPI_EXPORT
+  void setMirrorList(const std::list<std::shared_ptr<ModelAPI_Object> >& theObjects);
+
   /// List of mirrored objects
   SKETCHAPI_EXPORT
   std::list<std::shared_ptr<SketchAPI_SketchEntity> > mirrored() const;
index eb81b0d70cf649a22ecc108b2c0b42ceda85e27c..07e426a71467d839673f45a23ce52328b0b15178 100644 (file)
@@ -43,14 +43,13 @@ SketchAPI_Rotation::SketchAPI_Rotation(
 : ModelHighAPI_Interface(theFeature)
 {
   if (initialize()) {
-    fillAttribute(theObjects, rotationList());
     fillAttribute(theCenter, center());
     fillAttribute(theFullValue ? "FullAngle" : "SingleAngle", valueType());
     fillAttribute(theAngle, angle());
     fillAttribute(theReversed, reversed());
     fillAttribute(theNumberOfObjects, numberOfObjects());
 
-    execute(true);
+    setRotationList(theObjects);
   }
 }
 
@@ -59,6 +58,13 @@ SketchAPI_Rotation::~SketchAPI_Rotation()
 
 }
 
+void SketchAPI_Rotation::setRotationList(
+    const std::list<std::shared_ptr<ModelAPI_Object> >& theObjects)
+{
+  fillAttribute(theObjects, rotationList());
+  execute(true);
+}
+
 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_Rotation::rotated() const
 {
   std::list<ObjectPtr> aList = rotatedObjects()->list();
@@ -89,49 +95,77 @@ void SketchAPI_Rotation::dump(ModelHighAPI_Dumper& theDumper) const
   bool isReversed = reversed()->value();
 
   // Check all attributes are already dumped. If not, store the constraint as postponed.
-  if (!theDumper.isDumped(aCenter) || !theDumper.isDumped(aRotObjects)) {
+  size_t aFirstNotDumped = theDumper.indexOfFirstNotDumped(aRotObjects);
+  if (!theDumper.isDumped(aCenter) || aFirstNotDumped == 0) {
     theDumper.postpone(aBase);
     return;
   }
 
-  theDumper << aBase << " = " << aSketchName << ".addRotation("
-            << aRotObjects << ", " << aCenter << ", " << anAngle << ", " << aNbCopies;
-  if (isFullValue || isReversed)
-  {
-    theDumper << ", " << isFullValue;
-    if (isReversed)
-      theDumper << ", " << isReversed;
+  // the number of dumped aRotObjects is not changed, no need to dump anything
+  static std::map<FeaturePtr, size_t> aNbDumpedArguments;
+  std::map<FeaturePtr, size_t>::iterator aFound = aNbDumpedArguments.find(aBase);
+  if (aFound != aNbDumpedArguments.end() && aFound->second == aFirstNotDumped) {
+    theDumper.postpone(aBase);
+    return;
+  }
+  else
+    aNbDumpedArguments[aBase] = aFirstNotDumped;
+
+  if (theDumper.isDumped(aBase)) {
+    // the feature is already dumped, but it was postponed, because of some arguments
+    // were not dumped yet, thus, it is necessary to update the list of rotated objects
+    theDumper << "\n### Update " << aBase->getKind() << std::endl;
+    theDumper << aBase << ".setRotationList(" << aRotObjects << ")" << std::endl;
+  }
+  else {
+    // the feature is not dumped yet, make the full dump
+    theDumper << aBase << " = " << aSketchName << ".addRotation("
+              << aRotObjects << ", " << aCenter << ", " << anAngle << ", " << aNbCopies;
+    if (isFullValue || isReversed)
+    {
+      theDumper << ", " << isFullValue;
+      if (isReversed)
+        theDumper << ", " << isReversed;
+    }
+    theDumper << ")" << std::endl;
   }
-  theDumper << ")" << std::endl;
 
   // Dump variables for a list of rotated features
   theDumper << "[";
   std::list<std::shared_ptr<SketchAPI_SketchEntity> > aList = rotated();
   std::list<std::shared_ptr<SketchAPI_SketchEntity> >::const_iterator anIt = aList.begin();
-  for (; anIt != aList.end(); ++anIt) {
-    if (anIt != aList.begin())
-      theDumper << ", ";
-    theDumper << (*anIt)->feature();
-  }
+  for (size_t anIndex = 0; anIndex < aFirstNotDumped; ++anIndex)
+    for (int i = 1; i < aNbCopies->value() && anIt != aList.end(); ++i, ++anIt) {
+      if (anIt != aList.begin())
+        theDumper << ", ";
+      theDumper << (*anIt)->feature();
+    }
   theDumper << "] = " << theDumper.name(aBase) << ".rotated()" << std::endl;
 
-  // Set necessary "auxiliary" flag for rotated features
-  // (flag is set if it differs to base entity)
-  std::list<ObjectPtr> aRotList = aRotObjects->list();
-  std::list<ObjectPtr>::const_iterator aRIt = aRotList.begin();
-  anIt = aList.begin();
-  for (; aRIt != aRotList.end(); ++aRIt) {
-    FeaturePtr aFeature = ModelAPI_Feature::feature(*aRIt);
-    if (!aFeature)
-      continue;
-    bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
-
-    for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) {
-      aFeature = (*anIt)->feature();
-      bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
-      if (aFeatAux != aBaseAux)
-        theDumper << theDumper.name((*anIt)->feature(), false)
-                  << ".setAuxiliary(" << aFeatAux << ")" <<std::endl;
+  if (theDumper.isDumped(aRotObjects)) {
+    aNbDumpedArguments.erase(aBase);
+    // Set necessary "auxiliary" flag for rotated features
+    // (flag is set if it differs to base entity)
+    std::list<ObjectPtr> aRotList = aRotObjects->list();
+    std::list<ObjectPtr>::const_iterator aRIt = aRotList.begin();
+    anIt = aList.begin();
+    for (; aRIt != aRotList.end(); ++aRIt) {
+      FeaturePtr aFeature = ModelAPI_Feature::feature(*aRIt);
+      if (!aFeature)
+        continue;
+      bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
+
+      for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) {
+        aFeature = (*anIt)->feature();
+        bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
+        if (aFeatAux != aBaseAux)
+          theDumper << theDumper.name((*anIt)->feature(), false)
+                    << ".setAuxiliary(" << aFeatAux << ")" << std::endl;
+      }
     }
   }
+  else {
+    // If all refereced objects are not dumped yet, mark the feature as postponed.
+    theDumper.postpone(aBase);
+  }
 }
index ac3392f929996a1a39c81759bb8e82a91259975f..e9899d001021bbed51df3911152d8a63ed499983 100644 (file)
@@ -78,6 +78,10 @@ public:
               ModelAPI_AttributeRefList, /** Rotated objects */
   )
 
+  /// Set list of objects to be rotated
+  SKETCHAPI_EXPORT
+  void setRotationList(const std::list<std::shared_ptr<ModelAPI_Object> >& theObjects);
+
   /// List of rotated objects
   SKETCHAPI_EXPORT
   std::list<std::shared_ptr<SketchAPI_SketchEntity> > rotated() const;
index 660286d4530e95fafd21a06b36701aacefea4644..3c95ce76b5fa140f2cf48f5a0a349ff4f673e2d8 100644 (file)
@@ -42,19 +42,24 @@ SketchAPI_Translation::SketchAPI_Translation(
 : ModelHighAPI_Interface(theFeature)
 {
   if (initialize()) {
-    fillAttribute(theObjects, translationList());
     fillAttribute(thePoint1, startPoint());
     fillAttribute(thePoint2, endPoint());
     fillAttribute(theNumberOfObjects, numberOfObjects());
     fillAttribute(theFullValue ? "FullValue" : "SingleValue", valueType());
 
-    execute(true);
+    setTranslationList(theObjects);
   }
 }
 
 SketchAPI_Translation::~SketchAPI_Translation()
 {
+}
 
+void SketchAPI_Translation::setTranslationList(
+    const std::list<std::shared_ptr<ModelAPI_Object> >& theObjects)
+{
+  fillAttribute(theObjects, translationList());
+  execute(true);
 }
 
 std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_Translation::translated() const
@@ -86,46 +91,73 @@ void SketchAPI_Translation::dump(ModelHighAPI_Dumper& theDumper) const
   bool isFullValue = valueType()->value() != "SingleValue";
 
   // Check all attributes are already dumped. If not, store the constraint as postponed.
-  if (!theDumper.isDumped(aStart) || !theDumper.isDumped(aEnd) ||
-      !theDumper.isDumped(aTransObjects)) {
+  size_t aFirstNotDumped = theDumper.indexOfFirstNotDumped(aTransObjects);
+  if (!theDumper.isDumped(aStart) || !theDumper.isDumped(aEnd) || aFirstNotDumped == 0) {
     theDumper.postpone(aBase);
     return;
   }
 
-  theDumper << aBase << " = " << aSketchName << ".addTranslation("
-            << aTransObjects << ", " << aStart << ", " << aEnd << ", " << aNbCopies;
-  if (isFullValue)
-    theDumper << ", " << isFullValue;
-  theDumper << ")" << std::endl;
+  // the number of dumped aTransObjects is not changed, no need to dump anything
+  static std::map<FeaturePtr, size_t> aNbDumpedArguments;
+  std::map<FeaturePtr, size_t>::iterator aFound = aNbDumpedArguments.find(aBase);
+  if (aFound != aNbDumpedArguments.end() && aFound->second == aFirstNotDumped) {
+    theDumper.postpone(aBase);
+    return;
+  }
+  else
+    aNbDumpedArguments[aBase] = aFirstNotDumped;
+
+  if (theDumper.isDumped(aBase)) {
+    // the feature is already dumped, but it was postponed, because of some arguments
+    // were not dumped yet, thus, it is necessary to update the list of rotated objects
+    theDumper << "\n### Update " << aBase->getKind() << std::endl;
+    theDumper << aBase << ".setTranslationList(" << aTransObjects << ")" << std::endl;
+  }
+  else {
+    // the feature is not dumped yet, make the full dump
+    theDumper << aBase << " = " << aSketchName << ".addTranslation("
+              << aTransObjects << ", " << aStart << ", " << aEnd << ", " << aNbCopies;
+    if (isFullValue)
+      theDumper << ", " << isFullValue;
+    theDumper << ")" << std::endl;
+  }
 
   // Dump variables for a list of translated features
   theDumper << "[";
   std::list<std::shared_ptr<SketchAPI_SketchEntity> > aList = translated();
   std::list<std::shared_ptr<SketchAPI_SketchEntity> >::const_iterator anIt = aList.begin();
-  for (; anIt != aList.end(); ++anIt) {
-    if (anIt != aList.begin())
-      theDumper << ", ";
-    theDumper << (*anIt)->feature();
-  }
+  for (size_t anIndex = 0; anIndex < aFirstNotDumped; ++anIndex)
+    for (int i = 1; i < aNbCopies->value() && anIt != aList.end(); ++i, ++anIt) {
+      if (anIt != aList.begin())
+        theDumper << ", ";
+      theDumper << (*anIt)->feature();
+    }
   theDumper << "] = " << theDumper.name(aBase) << ".translated()" << std::endl;
 
-  // Set necessary "auxiliary" flag for translated features
-  // (flag is set if it differs to base entity)
-  std::list<ObjectPtr> aTransList = aTransObjects->list();
-  std::list<ObjectPtr>::const_iterator aTrIt = aTransList.begin();
-  anIt = aList.begin();
-  for (; aTrIt != aTransList.end(); ++aTrIt) {
-    FeaturePtr aFeature = ModelAPI_Feature::feature(*aTrIt);
-    if (!aFeature)
-      continue;
-    bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
-
-    for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) {
-      aFeature = (*anIt)->feature();
-      bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
-      if (aFeatAux != aBaseAux)
-        theDumper << theDumper.name((*anIt)->feature(), false)
-                  << ".setAuxiliary(" << aFeatAux << ")" <<std::endl;
+  if (theDumper.isDumped(aTransObjects)) {
+    aNbDumpedArguments.erase(aBase);
+    // Set necessary "auxiliary" flag for translated features
+    // (flag is set if it differs to base entity)
+    std::list<ObjectPtr> aTransList = aTransObjects->list();
+    std::list<ObjectPtr>::const_iterator aTrIt = aTransList.begin();
+    anIt = aList.begin();
+    for (; aTrIt != aTransList.end(); ++aTrIt) {
+      FeaturePtr aFeature = ModelAPI_Feature::feature(*aTrIt);
+      if (!aFeature)
+        continue;
+      bool aBaseAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
+
+      for (int i = 1; i < aNbCopies->value(); ++i, ++anIt) {
+        aFeature = (*anIt)->feature();
+        bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
+        if (aFeatAux != aBaseAux)
+          theDumper << theDumper.name((*anIt)->feature(), false)
+                    << ".setAuxiliary(" << aFeatAux << ")" << std::endl;
+      }
     }
   }
+  else {
+    // If all refereced objects are not dumped yet, mark the feature as postponed.
+    theDumper.postpone(aBase);
+  }
 }
index 558fa5a504ac5bd0bf9e26bb227e3259ceef6361..32119218e7143095e97d402377dbb7a840df7d9f 100644 (file)
@@ -74,6 +74,10 @@ public:
               ModelAPI_AttributeRefList, /** Translationed objects */
   )
 
+  /// Set list of objects to be translated
+  SKETCHAPI_EXPORT
+  void setTranslationList(const std::list<std::shared_ptr<ModelAPI_Object> >& theObjects);
+
   /// List of translated objects
   SKETCHAPI_EXPORT
   std::list<std::shared_ptr<SketchAPI_SketchEntity> > translated() const;
index 3fcc4e38983062588a70b457fcd7732c1dbfbae7..b5cbdd3d3d490ec995b2b1a67fbb935129265fef 100644 (file)
@@ -233,6 +233,10 @@ ADD_UNIT_TESTS(
   Test3240.py
   Test19089.py
   Test19101.py
+  Test20274_1.py
+  Test20274_2.py
+  Test20274_3.py
+
   TestArcBehavior.py
   TestBSplineAddPole.py
   TestChangeSketchPlane1.py
index a8eb3d6c437facaeae818e0f32dabb3e872d8603..009e9ab448eeeee7116026f4797f1ebfe976debd 100644 (file)
@@ -62,6 +62,11 @@ void SketchPlugin_ConstraintMirror::execute()
   if (isUpdateFlushed)
     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
 
+  // Save the current feature of the document, because new features may appear while executing.
+  // In this case, they will become current. But if the number of copies is updated from outside
+  // of sketch (e.g. by parameter change), the history line should not hold in sketch.
+  keepCurrentFeature();
+
   std::shared_ptr<ModelAPI_Data> aData = data();
   AttributeRefListPtr aRefListOfShapes = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
       aData->attribute(SketchPlugin_Constraint::ENTITY_B()));
@@ -198,6 +203,8 @@ void SketchPlugin_ConstraintMirror::execute()
     }
   }
 
+  restoreCurrentFeature();
+
   // send events to update the sub-features by the solver
   if (isUpdateFlushed)
     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
index f3f8d2b891e543bdc309c36822ff4c5cad3774d5..4400e8e612ec3dab39301459019861d2b9712cdc 100644 (file)
@@ -127,6 +127,11 @@ void SketchPlugin_Offset::execute()
   if (isUpdateFlushed)
     Events_Loop::loop()->setFlushed(anUpdateEvent, false);
 
+  // Save the current feature of the document, because new features may appear while executing.
+  // In this case, they will become current. But if the number of copies is updated from outside
+  // of sketch (e.g. by parameter change), the history line should not hold in sketch.
+  keepCurrentFeature();
+
   // 5. Gather wires and make offset for each wire
   ListOfMakeShape anOffsetAlgos;
   std::set<FeaturePtr> aProcessedEdgesSet;
@@ -199,6 +204,8 @@ void SketchPlugin_Offset::execute()
   //    created features in CREATED_ID() to remove them on next execute()
   addToSketch(anOffsetAlgos);
 
+  restoreCurrentFeature();
+
   // send events to update the sub-features by the solver
   if (isUpdateFlushed)
     Events_Loop::loop()->setFlushed(anUpdateEvent, true);
diff --git a/src/SketchPlugin/Test/Test20274_1.py b/src/SketchPlugin/Test/Test20274_1.py
new file mode 100644 (file)
index 0000000..353241a
--- /dev/null
@@ -0,0 +1,112 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomAlgoAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Part
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+
+### Create Sketch
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+
+### Create SketchLine
+SketchLine_1 = Sketch_1.addLine(50, 0, 41.0188620508502, 41.01886205085074)
+
+### Create SketchProjection
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_2 = SketchProjection_1.createdFeature()
+Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result())
+
+### Create SketchLine
+SketchLine_3 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 0, 50)
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.startPoint())
+
+### Create SketchProjection
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_4 = SketchProjection_2.createdFeature()
+Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.result())
+
+### Create SketchLine
+SketchLine_5 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 70.71067811865088, 70.71067811866516)
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.startPoint())
+
+### Create SketchCircle
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 100)
+SketchCircle_1.setAuxiliary(True)
+Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).startPoint(), SketchCircle_1.center())
+Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).endPoint(), SketchCircle_1.results()[1])
+Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchCircle_1.results()[1])
+Sketch_1.setMiddlePoint(SketchLine_1.startPoint(), SketchLine_2.result())
+Sketch_1.setMiddlePoint(SketchLine_3.endPoint(), SketchLine_4.result())
+Sketch_1.setEqual(SketchLine_1.result(), SketchLine_3.result())
+Sketch_1.setEqual(SketchLine_1.result(), SketchLine_5.result())
+Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_5.result())
+
+### Create SketchMultiRotation
+SketchMultiRotation_1_objects = [SketchLine_1.result(), SketchLine_3.result(), SketchLine_5.result()]
+SketchMultiRotation_1 = Sketch_1.addRotation(SketchMultiRotation_1_objects, SketchCircle_1.center(), 360, 4, True)
+[SketchLine_6, SketchLine_7, SketchLine_8, SketchLine_9, SketchLine_10, SketchLine_11, SketchLine_12, SketchLine_13, SketchLine_14] = SketchMultiRotation_1.rotated()
+model.do()
+
+### Create Extrusion
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 100, 0, "Edges")
+model.end()
+
+### Store volumes of sub-shapes as a reference
+TOLERANCE = 1.e-7
+REFERENCE = []
+resultExtrusion_1 = Extrusion_1.result()
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  REFERENCE.append(GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()))
+
+### Add new edges to Sketch_1 then check the Extrusion and update reference data
+model.begin()
+SketchLine_6 = Sketch_1.addLine(70.71067811865088, 70.71067811866516, 77.78174593052023, 77.78174593052023)
+Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+Sketch_1.setCollinear(SketchLine_5.result(), SketchLine_6.result())
+Sketch_1.setLength(SketchLine_6.result(), 10)
+
+SketchMultiRotation_1.rotationList().append(SketchLine_6.defaultResult())
+model.end()
+
+resultExtrusion_1 = Extrusion_1.result()
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())
+  if (ind < len(REFERENCE)):
+    assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE)
+  else:
+    REFERENCE.append(area)
+
+assert(model.checkPythonDump(model.CHECK_NAMING))
+
+### Check results after dump
+resultExtrusion_1 = Extrusion_1.result()
+assert(len(REFERENCE) == resultExtrusion_1.numberOfSubs())
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())
+  assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE)
diff --git a/src/SketchPlugin/Test/Test20274_2.py b/src/SketchPlugin/Test/Test20274_2.py
new file mode 100644 (file)
index 0000000..e239d3b
--- /dev/null
@@ -0,0 +1,112 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomAlgoAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Part
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+
+### Create Sketch
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+
+### Create SketchLine
+SketchLine_1 = Sketch_1.addLine(50, 0, 41.0188620508502, 41.01886205085074)
+
+### Create SketchProjection
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_2 = SketchProjection_1.createdFeature()
+Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result())
+
+### Create SketchLine
+SketchLine_3 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 0, 50)
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.startPoint())
+
+### Create SketchProjection
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_4 = SketchProjection_2.createdFeature()
+Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.result())
+
+### Create SketchLine
+SketchLine_5 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 70.71067811865088, 70.71067811866516)
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.startPoint())
+
+### Create SketchCircle
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 100)
+SketchCircle_1.setAuxiliary(True)
+Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).startPoint(), SketchCircle_1.center())
+Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).endPoint(), SketchCircle_1.results()[1])
+Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchCircle_1.results()[1])
+Sketch_1.setMiddlePoint(SketchLine_1.startPoint(), SketchLine_2.result())
+Sketch_1.setMiddlePoint(SketchLine_3.endPoint(), SketchLine_4.result())
+Sketch_1.setEqual(SketchLine_1.result(), SketchLine_3.result())
+Sketch_1.setEqual(SketchLine_1.result(), SketchLine_5.result())
+Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_5.result())
+
+### Create SketchMultiTranslation
+SketchMultiTranslation_1_objects = [SketchLine_1.result(), SketchLine_3.result(), SketchLine_5.result()]
+SketchMultiTranslation_1 = Sketch_1.addTranslation(SketchMultiTranslation_1_objects, SketchAPI_Line(SketchLine_4).startPoint(), SketchLine_5.endPoint(), 2)
+[SketchLine_7, SketchLine_8, SketchLine_9] = SketchMultiTranslation_1.translated()
+model.do()
+
+### Create Extrusion
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 100, 0, "Edges")
+model.end()
+
+### Store volumes of sub-shapes as a reference
+TOLERANCE = 1.e-7
+REFERENCE = []
+resultExtrusion_1 = Extrusion_1.result()
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  REFERENCE.append(GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()))
+
+### Add new edges to Sketch_1 then check the Extrusion and update reference data
+model.begin()
+SketchLine_6 = Sketch_1.addLine(70.71067811865088, 70.71067811866516, 77.78174593052023, 77.78174593052023)
+Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+Sketch_1.setCollinear(SketchLine_5.result(), SketchLine_6.result())
+Sketch_1.setLength(SketchLine_6.result(), 10)
+
+SketchMultiTranslation_1.translationList().append(SketchLine_6.defaultResult())
+model.end()
+
+resultExtrusion_1 = Extrusion_1.result()
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())
+  if (ind < len(REFERENCE)):
+    assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE)
+  else:
+    REFERENCE.append(area)
+
+assert(model.checkPythonDump(model.CHECK_NAMING))
+
+### Check results after dump
+resultExtrusion_1 = Extrusion_1.result()
+assert(len(REFERENCE) == resultExtrusion_1.numberOfSubs())
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())
+  assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE)
diff --git a/src/SketchPlugin/Test/Test20274_3.py b/src/SketchPlugin/Test/Test20274_3.py
new file mode 100644 (file)
index 0000000..de30b85
--- /dev/null
@@ -0,0 +1,112 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomAlgoAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Part
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+
+### Create Sketch
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+
+### Create SketchLine
+SketchLine_1 = Sketch_1.addLine(50, 0, 41.0188620508502, 41.01886205085074)
+
+### Create SketchProjection
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_2 = SketchProjection_1.createdFeature()
+Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result())
+
+### Create SketchLine
+SketchLine_3 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 0, 50)
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.startPoint())
+
+### Create SketchProjection
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_4 = SketchProjection_2.createdFeature()
+Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.result())
+
+### Create SketchLine
+SketchLine_5 = Sketch_1.addLine(41.0188620508502, 41.01886205085074, 70.71067811865088, 70.71067811866516)
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.startPoint())
+
+### Create SketchCircle
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 100)
+SketchCircle_1.setAuxiliary(True)
+Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).startPoint(), SketchCircle_1.center())
+Sketch_1.setCoincident(SketchAPI_Line(SketchLine_2).endPoint(), SketchCircle_1.results()[1])
+Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchCircle_1.results()[1])
+Sketch_1.setMiddlePoint(SketchLine_1.startPoint(), SketchLine_2.result())
+Sketch_1.setMiddlePoint(SketchLine_3.endPoint(), SketchLine_4.result())
+Sketch_1.setEqual(SketchLine_1.result(), SketchLine_3.result())
+Sketch_1.setEqual(SketchLine_1.result(), SketchLine_5.result())
+Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_5.result())
+
+### Create SketchConstraintMirror
+SketchConstraintMirror_1_objects = [SketchLine_1.result(), SketchLine_3.result(), SketchLine_5.result()]
+SketchConstraintMirror_1 = Sketch_1.addMirror(SketchLine_2.result(), SketchConstraintMirror_1_objects)
+[SketchLine_7, SketchLine_8, SketchLine_9] = SketchConstraintMirror_1.mirrored()
+model.do()
+
+### Create Extrusion
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 100, 0, "Edges")
+model.end()
+
+### Store volumes of sub-shapes as a reference
+TOLERANCE = 1.e-7
+REFERENCE = []
+resultExtrusion_1 = Extrusion_1.result()
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  REFERENCE.append(GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape()))
+
+### Add new edges to Sketch_1 then check the Extrusion and update reference data
+model.begin()
+SketchLine_6 = Sketch_1.addLine(70.71067811865088, 70.71067811866516, 77.78174593052023, 77.78174593052023)
+Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+Sketch_1.setCollinear(SketchLine_5.result(), SketchLine_6.result())
+Sketch_1.setLength(SketchLine_6.result(), 10)
+
+SketchConstraintMirror_1.mirrorList().append(SketchLine_6.defaultResult())
+model.end()
+
+resultExtrusion_1 = Extrusion_1.result()
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())
+  if (ind < len(REFERENCE)):
+    assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE)
+  else:
+    REFERENCE.append(area)
+
+assert(model.checkPythonDump(model.CHECK_NAMING))
+
+### Check results after dump
+resultExtrusion_1 = Extrusion_1.result()
+assert(len(REFERENCE) == resultExtrusion_1.numberOfSubs())
+for ind in range(resultExtrusion_1.numberOfSubs()):
+  area = GeomAlgoAPI_ShapeTools.volume(resultExtrusion_1.subResult(ind).resultSubShapePair()[0].shape())
+  assert(math.fabs(REFERENCE[ind] - area) < TOLERANCE)