]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Task 3.2. To keep compounds’ sub-shapes for all operations (issue #3139)
authorazv <azv@opencascade.com>
Thu, 13 Feb 2020 05:29:24 +0000 (08:29 +0300)
committerazv <azv@opencascade.com>
Thu, 13 Feb 2020 05:29:24 +0000 (08:29 +0300)
Refactoring the Placement feature.

23 files changed:
src/FeaturesAPI/FeaturesAPI.i
src/FeaturesAPI/FeaturesAPI_Placement.cpp
src/FeaturesAPI/FeaturesAPI_Placement.h
src/FeaturesPlugin/CMakeLists.txt
src/FeaturesPlugin/FeaturesPlugin_Placement.cpp
src/FeaturesPlugin/FeaturesPlugin_ValidatorTransform.cpp
src/FeaturesPlugin/FeaturesPlugin_msg_en.ts
src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_1.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_2.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_3.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_4.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_5.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_6.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_1.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_2.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_3.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_4.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_5.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_6.py [new file with mode: 0644]
src/GeomAPI/GeomAPI_ShapeHierarchy.cpp
src/GeomAPI/GeomAPI_ShapeHierarchy.h
src/PythonAPI/model/tests/tests.py

index a77c6a1c4b1692a44593081af896bf84ba17b4dd..01ca8e21a9c7eb0048841e71ed6ab076bee7c92f 100644 (file)
@@ -42,6 +42,7 @@
 %feature("kwargs") addCut;
 %feature("kwargs") addFuse;
 %feature("kwargs") addPartition;
+%feature("kwargs") addPlacement;
 %feature("kwargs") addSplit;
 %feature("kwargs") addSmash;
 %feature("kwargs") addUnion;
index 54cb9fd13dc94275955d50fa65e14627bc0e158e..96869875bf887d0da83a8816d251c80f9a5535a5 100644 (file)
@@ -108,8 +108,16 @@ void FeaturesAPI_Placement::dump(ModelHighAPI_Dumper& theDumper) const
   AttributeBooleanPtr anAttrCentering = aBase->boolean(FeaturesPlugin_Placement::CENTERING_ID());
 
   theDumper << aBase << " = model.addPlacement(" << aDocName << ", "
-            << anAttrObjects << ", " << anAttrStartShape << ", " << anAttrEndShape << ", "
-            << anAttrReverse << ", " << anAttrCentering << ")" << std::endl;
+            << anAttrObjects << ", " << anAttrStartShape << ", " << anAttrEndShape;
+
+  if (anAttrReverse->value())
+    theDumper << ", reverse = " << anAttrReverse;
+  if (anAttrCentering->value())
+    theDumper << ", centering = " << anAttrCentering;
+  if (!aBase->data()->version().empty())
+    theDumper << ", keepSubResults = True";
+
+  theDumper << ")" << std::endl;
 }
 
 //==================================================================================================
@@ -118,9 +126,12 @@ PlacementPtr addPlacement(const std::shared_ptr<ModelAPI_Document>& thePart,
                           const ModelHighAPI_Selection& theStartShape,
                           const ModelHighAPI_Selection& theEndShape,
                           const bool theReverseDirection,
-                          const bool theCentering)
+                          const bool theCentering,
+                          const bool keepSubResults)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(FeaturesAPI_Placement::ID());
+  if (!keepSubResults)
+    aFeature->data()->setVersion("");
   return PlacementPtr(new FeaturesAPI_Placement(aFeature,
                                                 theObjects,
                                                 theStartShape,
index e749c148927ef03d521ed1f343b8dcd441b7156c..aa0667a9d6cf015e9a6ce46b82a1db611f9f18f7 100644 (file)
@@ -100,7 +100,8 @@ PlacementPtr addPlacement(const std::shared_ptr<ModelAPI_Document>& thePart,
                           const std::list<ModelHighAPI_Selection>& theObjects,
                           const ModelHighAPI_Selection& theStartShape,
                           const ModelHighAPI_Selection& theEndShape,
-                          const bool theReverseDirection = false,
-                          const bool theCentering = false);
+                          const bool reverse = false,
+                          const bool centering = false,
+                          const bool keepSubResults = false);
 
 #endif // FeaturesAPI_Placement_H_
index bdca89390e6009fd40a278b100582831cdef192d..69defc8b56064bee1933fe9b118899d7dfb37d07 100644 (file)
@@ -569,4 +569,16 @@ ADD_UNIT_TESTS(TestExtrusion.py
                Test3137_1.py
                Test3137_2.py
                Test3139.py
+               TestPlacement_MultiLevelCompound_v0_1.py
+               TestPlacement_MultiLevelCompound_v0_2.py
+               TestPlacement_MultiLevelCompound_v0_3.py
+               TestPlacement_MultiLevelCompound_v0_4.py
+               TestPlacement_MultiLevelCompound_v0_5.py
+               TestPlacement_MultiLevelCompound_v0_6.py
+               TestPlacement_MultiLevelCompound_v95_1.py
+               TestPlacement_MultiLevelCompound_v95_2.py
+               TestPlacement_MultiLevelCompound_v95_3.py
+               TestPlacement_MultiLevelCompound_v95_4.py
+               TestPlacement_MultiLevelCompound_v95_5.py
+               TestPlacement_MultiLevelCompound_v95_6.py
 )
index 0acf3a84e07c22da2d7d80bc2c0757fde884230a..df5d532ab313efd979bf394f2fbcb86c4a33747d 100644 (file)
 #include <ModelAPI_AttributeBoolean.h>
 #include <ModelAPI_AttributeSelectionList.h>
 #include <ModelAPI_BodyBuilder.h>
+#include <ModelAPI_Tools.h>
 
 #include <GeomAPI_Edge.h>
 #include <GeomAPI_Face.h>
 #include <GeomAPI_Pln.h>
+#include <GeomAPI_ShapeHierarchy.h>
 #include <GeomAPI_ShapeIterator.h>
+
+#include <GeomAlgoAPI_MakeShapeList.h>
 #include <GeomAlgoAPI_Placement.h>
 #include <GeomAlgoAPI_Transform.h>
 #include <GeomAlgoAPI_Tools.h>
 
 #include <FeaturesPlugin_Tools.h>
 
+static const std::string PLACEMENT_VERSION_1("v9.5");
+
 FeaturesPlugin_Placement::FeaturesPlugin_Placement()
 {
 }
 
 void FeaturesPlugin_Placement::initAttributes()
 {
-
   AttributeSelectionListPtr aSelection =
     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(data()->addAttribute(
     OBJECTS_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()));
@@ -52,13 +57,20 @@ void FeaturesPlugin_Placement::initAttributes()
   data()->addAttribute(END_SHAPE_ID(), ModelAPI_AttributeSelection::typeId());
   data()->addAttribute(REVERSE_ID(), ModelAPI_AttributeBoolean::typeId());
   data()->addAttribute(CENTERING_ID(), ModelAPI_AttributeBoolean::typeId());
+
+  if (!aSelection->isInitialized()) {
+    // new feature, not read from file
+    data()->setVersion(PLACEMENT_VERSION_1);
+  }
 }
 
 void FeaturesPlugin_Placement::execute()
 {
+  bool isKeepSubShapes = data()->version() == PLACEMENT_VERSION_1;
+
   // Getting objects.
-  ListOfShape anObjects;
-  std::list<ResultPtr> aContextes;
+  GeomAPI_ShapeHierarchy anObjects;
+  std::list<ResultPtr> aParts;
   AttributeSelectionListPtr anObjectsSelList = selectionList(OBJECTS_LIST_ID());
   if(anObjectsSelList->size() == 0) {
     return;
@@ -70,8 +82,15 @@ void FeaturesPlugin_Placement::execute()
     if(!anObject.get()) { // may be for not-activated parts
       return;
     }
-    anObjects.push_back(anObject);
-    aContextes.push_back(anObjectAttr->context());
+    ResultPtr aContext = anObjectAttr->context();
+    if (aContext->groupName() == ModelAPI_ResultPart::group())
+      aParts.push_back(aContext);
+    else {
+      // store full shape hierarchy for the corresponding version only
+      anObjects.addObject(anObject);
+      if (isKeepSubShapes)
+        ModelAPI_Tools::fillShapeHierarchy(anObject, aContext, anObjects);
+    }
   }
 
   // Verify the start shape
@@ -140,40 +159,45 @@ void FeaturesPlugin_Placement::execute()
   }
   std::shared_ptr<GeomAPI_Trsf> aTrsf = aPlacementAlgo.transformation();
 
-  // Applying transformation to each object.
   int aResultIndex = 0;
   std::string anError;
-  std::list<ResultPtr>::iterator aContext = aContextes.begin();
-  for(ListOfShape::iterator anObjectsIt = anObjects.begin(); anObjectsIt != anObjects.end();
-      anObjectsIt++, aContext++) {
-
-    // for part results just set transformation
-    if (aContext->get() && (*aContext)->groupName() == ModelAPI_ResultPart::group()) {
-      ResultPartPtr anOrigin = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aContext);
-      ResultPartPtr aResultPart = document()->copyPart(anOrigin, data(), aResultIndex);
-      aResultPart->setTrsf(aContextRes, aTrsf);
-      setResult(aResultPart, aResultIndex);
-    } else {
-      std::shared_ptr<GeomAPI_Shape> aBaseShape = *anObjectsIt;
-      std::shared_ptr<GeomAlgoAPI_Transform> aTransformAlgo(new GeomAlgoAPI_Transform(aBaseShape,
-                                                                                      aTrsf));
-
-      // Checking that the algorithm worked properly.
-      if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aTransformAlgo, getKind(), anError)) {
-        setError(anError);
-        break;
-      }
+  for (std::list<ResultPtr>::iterator aPRes = aParts.begin(); aPRes != aParts.end(); ++aPRes) {
+    // Applying transformation to each part.
+    ResultPartPtr anOrigin = std::dynamic_pointer_cast<ModelAPI_ResultPart>(*aPRes);
+    ResultPartPtr aResultPart = document()->copyPart(anOrigin, data(), aResultIndex);
+    aResultPart->setTrsf(aContextRes, aTrsf);
+    setResult(aResultPart, aResultIndex++);
+  }
+
+  // Collect transformations for each object.
+  std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList);
 
-      //LoadNamingDS
-      ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
+  for(GeomAPI_ShapeHierarchy::iterator anObjectsIt = anObjects.begin();
+      anObjectsIt != anObjects.end(); anObjectsIt++) {
+    std::shared_ptr<GeomAPI_Shape> aBaseShape = *anObjectsIt;
+    std::shared_ptr<GeomAlgoAPI_Transform> aTransformAlgo(
+        new GeomAlgoAPI_Transform(aBaseShape, aTrsf));
 
-      ListOfShape aShapes;
-      aShapes.push_back(aBaseShape);
-      FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, aShapes, ListOfShape(),
-                                               aTransformAlgo, aTransformAlgo->shape(), "Placed");
-      setResult(aResultBody, aResultIndex);
+    // Checking that the algorithm worked properly.
+    if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aTransformAlgo, getKind(), anError)) {
+      setError(anError);
+      break;
     }
-    aResultIndex++;
+
+    anObjects.markModified(aBaseShape, aTransformAlgo->shape());
+    aMakeShapeList->appendAlgo(aTransformAlgo);
+  }
+
+  // Build results of the operation.
+  const ListOfShape& anOriginalShapes = anObjects.objects();
+  ListOfShape aTopLevel;
+  anObjects.topLevelObjects(aTopLevel);
+  for (ListOfShape::iterator anIt = aTopLevel.begin(); anIt != aTopLevel.end(); ++anIt) {
+    //LoadNamingDS
+    ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
+    FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, anOriginalShapes, ListOfShape(),
+                                             aMakeShapeList, *anIt, "Placed");
+    setResult(aResultBody, aResultIndex++);
   }
 
   // Remove the rest results if there were produced in the previous pass.
index 03a55a611aecd70abc07ba903f0b6ee0a6467245..ebbd18ecbdc9c86559780a084d10b0c4b4cf67f5 100644 (file)
 
 #include <Events_InfoMessage.h>
 
-#include "ModelAPI_AttributeSelectionList.h"
-#include "ModelAPI_ResultPart.h"
-#include "ModelAPI_ResultBody.h"
-#include "ModelAPI_Session.h"
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_ResultPart.h>
+#include <ModelAPI_ResultBody.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Tools.h>
 
 bool FeaturesPlugin_ValidatorTransform::isValid(const AttributePtr& theAttribute,
                                                 const std::list<std::string>& theArguments,
@@ -46,6 +47,7 @@ bool FeaturesPlugin_ValidatorTransform::isValid(const AttributePtr& theAttribute
   DocumentPtr aDocument = theAttribute->owner()->document();
   SessionPtr aMgr = ModelAPI_Session::get();
   bool isPartSetDocument = aDocument == aMgr->moduleDocument();
+  bool isCheckCompsolid = !theAttribute->owner()->data()->version().empty();
 
   std::string anErrorGroupName;
   for(int i = 0; i < aCurSelList->size() && aValid; i++) {
@@ -67,11 +69,14 @@ bool FeaturesPlugin_ValidatorTransform::isValid(const AttributePtr& theAttribute
     }
     if (isPartSetDocument) // PartSet document: Result Part is valid
       aValid = aResult->groupName() == ModelAPI_ResultPart::group();
-    else { // Part document: Result CompSolid is valid
+    else { // Part document: Result CompSolid is valid, but the solid in compsolid is forbidden
       aValid = aResult->groupName() == ModelAPI_ResultBody::group();
-      if (aValid) {
-        ResultBodyPtr aComp = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aResult);
-        aValid = aComp.get() != NULL;
+      if (aValid && isCheckCompsolid) {
+        ResultBodyPtr aComp = ModelAPI_Tools::bodyOwner(aResult);
+        if (aComp && aComp->shape()->shapeType() == GeomAPI_Shape::COMPSOLID) {
+          theError = "Selecting a part of compsolid is forbidden.";
+          return false;
+        }
       }
     }
     if (!aValid)
index f383d98035cef258d14c5f80ecb450d0f9e49245..5ec922a053a0fbd8ce57bb2acaf2413c9647cb70 100644 (file)
       <translation>Objects from the %1 group can be selected in the %2 document, but an objects from the %3 group is selected.</translation>
     </message>
   </context>
+  <context>
+    <name>Placement:placement_objects_list:FeaturesPlugin_ValidatorTransform</name>
+    <message>
+      <source>Selecting a part of compsolid is forbidden.</source>
+      <translation>Selecting a part of compsolid is forbidden.</translation>
+    </message>
+  </context>
   <context>
     <name>Placement:Model_FeatureValidator</name>
     <message>
index 71f2863f671249a95bed5358f25a8386d9871607..df82833ecb0a1ff4d11e1768dba4b58997195b41 100644 (file)
       <translation>Les objets du groupe %1 peuvent ĂŞtre sĂ©lectionnĂ©s dans le document %2, mais un objet du groupe %3 est sĂ©lectionnĂ©.</translation>
     </message>
   </context>
+  <context>
+    <name>Placement:placement_objects_list:FeaturesPlugin_ValidatorTransform</name>
+    <message>
+      <source>Selecting a part of compsolid is forbidden.</source>
+      <translation>La sĂ©lection d'une partie de solide composite est interdite.</translation>
+    </message>
+  </context>
   <context>
     <name>Placement:Model_FeatureValidator</name>
     <message>
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_1.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_1.py
new file mode 100644 (file)
index 0000000..d93c509
--- /dev/null
@@ -0,0 +1,83 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "LinearCopy_2_1_1_1")], model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/To_Face"), False, True)
+
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [3])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [6])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [12])
+model.testResultsVolumes(Placement_1, [785.39816339745])
+
+TOLERANCE = 1.e-7
+REFERENCE = GeomAPI_Pnt(41.18033988749897, -50, 15)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_2.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_2.py
new file mode 100644 (file)
index 0000000..4791b85
--- /dev/null
@@ -0,0 +1,83 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_2_1_1")], model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/To_Face"), False, True)
+
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [2])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [6])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [12])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [24])
+model.testResultsVolumes(Placement_1, [1570.7963267949])
+
+TOLERANCE = 1.e-7
+REFERENCE = GeomAPI_Pnt(31.18033988749897, -50, 15)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_3.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_3.py
new file mode 100644 (file)
index 0000000..a52ff74
--- /dev/null
@@ -0,0 +1,84 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "Compound_2_1_1_5"), model.selection("SOLID", "Compound_2_1_2_1")], model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/From_Face"), model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), False, False)
+
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 2)
+model.testNbSubResults(Placement_1, [0, 0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [1, 1])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [5, 3])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [18, 6])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [36, 12])
+model.testResultsVolumes(Placement_1, [542.746463956, 785.39816339745])
+
+TOLERANCE = 1.e-7
+REFERENCE = [GeomAPI_Pnt(0, -50, 15), GeomAPI_Pnt(41.18033988749897, -50, 15)]
+for res, ref in zip(Placement_1.results(), REFERENCE):
+    midPoint = res.resultSubShapePair()[0].shape().middlePoint()
+    assert(midPoint.distance(ref) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_4.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_4.py
new file mode 100644 (file)
index 0000000..1a85277
--- /dev/null
@@ -0,0 +1,84 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("COMPSOLID", "Compound_2_1_1"), model.selection("SOLID", "Compound_2_1_3")], model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/From_Face"), model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), False, False)
+
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 2)
+model.testNbSubResults(Placement_1, [7, 0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [7, 1])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [38, 3])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [144, 6])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [288, 12])
+model.testResultsVolumes(Placement_1, [11963.80153655, 785.39816339745])
+
+TOLERANCE = 1.e-7
+REFERENCE = [GeomAPI_Pnt(4.5668653531, -50, 15), GeomAPI_Pnt(-20.59016994374951, -85.663220479, 15)]
+for res, ref in zip(Placement_1.results(), REFERENCE):
+    midPoint = res.resultSubShapePair()[0].shape().middlePoint()
+    assert(midPoint.distance(ref) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_5.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_5.py
new file mode 100644 (file)
index 0000000..c66e040
--- /dev/null
@@ -0,0 +1,187 @@
+# 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 salome.shaper import model
+from GeomAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchPoint_1 = Sketch_1.addPoint(44.29784155360136, 17.09942588468031)
+SketchArc_1 = Sketch_1.addArc(44.29784155360136, 17.09942588468031, 47.1668727423061, 12.27945348765633, 45.38299232715926, 22.60269052200972, False)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchArc_1.center())
+SketchLine_1 = Sketch_1.addLine(42.5764228403785, 22.14892077680068, 39.70739165167375, 16.41085839939117)
+SketchLine_2 = Sketch_1.addLine(39.70739165167375, 16.41085839939117, 43.03546783057126, 12.04993099255995)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(28.57555063949931, 12.96802097294547, 15.72229091410204, 12.96802097294547)
+SketchLine_4 = Sketch_1.addLine(15.72229091410204, 12.96802097294547, 15.72229091410204, 21.46035329151154)
+SketchLine_5 = Sketch_1.addLine(15.72229091410204, 21.46035329151154, 28.57555063949931, 21.46035329151154)
+SketchLine_6 = Sketch_1.addLine(28.57555063949931, 21.46035329151154, 28.57555063949931, 12.96802097294547)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchLine_7 = Sketch_1.addLine(-10.67279602198167, 14.78814178154371, -28.34602814440294, 14.78814178154371)
+SketchLine_8 = Sketch_1.addLine(-28.34602814440294, 14.78814178154371, -28.34602814440294, 25.13271321305362)
+SketchLine_9 = Sketch_1.addLine(-28.34602814440294, 25.13271321305362, -10.67279602198167, 25.13271321305362)
+SketchLine_10 = Sketch_1.addLine(-10.67279602198167, 25.13271321305362, -10.67279602198167, 14.78814178154371)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_10.endPoint(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint())
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_7.result())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_9.result())
+SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(-17.67323212242127, 25.13271321305362, -21.80463703415611, 14.78814178154371)
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchLine_11.startPoint(), SketchLine_9.result())
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_11.endPoint(), SketchLine_7.result())
+model.do()
+Vertex_1 = model.addVertex(Part_1_doc, [model.selection("VERTEX", "Sketch_1/SketchArc_1")], False)
+Edge_1 = model.addEdge(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchArc_1_2")], False)
+Wire_1 = model.addWire(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2")], False)
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_6r-SketchLine_5r-SketchLine_4r-SketchLine_3r")])
+Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_10r-SketchLine_9r-SketchLine_11f-SketchLine_7r"), model.selection("FACE", "Sketch_1/Face-SketchLine_11r-SketchLine_9r-SketchLine_8r-SketchLine_7r")])
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 50)
+Box_2 = model.addBox(Part_1_doc, 10, 10, 10)
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "Box_2_1"), model.selection("SOLID", "Cylinder_1_1")])
+Box_3 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_2 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_3_1")], model.selection("EDGE", "PartSet/OY"), 50)
+AngularCopy_1 = model.addMultiRotation(Part_1_doc, [model.selection("SOLID", "Translation_2_1")], model.selection("EDGE", "PartSet/OZ"), 30, 3)
+model.end()
+
+Z_SHIFT = 10
+TOLERANCE = 1.e-7
+
+model.begin()
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("VERTEX", "Vertex_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [1])
+model.testResultsVolumes(Placement_1, [0])
+refPoint = Vertex_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_2 = model.addPlacement(Part_1_doc, [model.selection("EDGE", "Edge_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_2, 1)
+model.testNbSubResults(Placement_2, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.EDGE, [1])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.VERTEX, [2])
+model.testResultsVolumes(Placement_2, [0])
+refPoint = Edge_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_2.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_3 = model.addPlacement(Part_1_doc, [model.selection("WIRE", "Wire_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_3, 1)
+model.testNbSubResults(Placement_3, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.EDGE, [2])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.VERTEX, [4])
+model.testResultsVolumes(Placement_3, [0])
+refPoint = Wire_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_3.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_4 = model.addPlacement(Part_1_doc, [model.selection("FACE", "Face_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_4, 1)
+model.testNbSubResults(Placement_4, [0])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.FACE, [1])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.EDGE, [4])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.VERTEX, [8])
+model.testResultsVolumes(Placement_4, [109.154152964914914])
+refPoint = Face_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_4.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_5 = model.addPlacement(Part_1_doc, [model.selection("SHELL", "Shell_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_5, 1)
+model.testNbSubResults(Placement_5, [0])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.FACE, [2])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.EDGE, [8])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.VERTEX, [16])
+model.testResultsVolumes(Placement_5, [182.822012116])
+refPoint = Shell_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_5.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_6 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "Translation_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_6, 1)
+model.testNbSubResults(Placement_6, [0])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.FACE, [6])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.EDGE, [24])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.VERTEX, [48])
+model.testResultsVolumes(Placement_6, [1000])
+refPoint = Translation_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_6.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_7 = model.addPlacement(Part_1_doc, [model.selection("COMPSOLID", "Partition_1_1")], model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Bottom"), model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Top"), False, False)
+model.testNbResults(Placement_7, 1)
+model.testNbSubResults(Placement_7, [3])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.SOLID, [3])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.FACE, [17])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.EDGE, [66])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.VERTEX, [132])
+model.testResultsVolumes(Placement_7, [1589.0486226])
+refPoint = Partition_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_7.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_8 = model.addPlacement(Part_1_doc, [model.selection("COMPOUND", "AngularCopy_1_1")],model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Bottom"), model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Top"), False, False)
+model.testNbResults(Placement_8, 1)
+model.testNbSubResults(Placement_8, [3])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.SOLID, [3])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.FACE, [18])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.EDGE, [72])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.VERTEX, [144])
+model.testResultsVolumes(Placement_8, [3000])
+refPoint = AngularCopy_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_8.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_6.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v0_6.py
new file mode 100644 (file)
index 0000000..eae3e70
--- /dev/null
@@ -0,0 +1,195 @@
+# 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 salome.shaper import model
+from GeomAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchPoint_1 = Sketch_1.addPoint(44.29784155360136, 17.09942588468031)
+SketchArc_1 = Sketch_1.addArc(44.29784155360136, 17.09942588468031, 47.1668727423061, 12.27945348765633, 45.38299232715926, 22.60269052200972, False)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchArc_1.center())
+SketchLine_1 = Sketch_1.addLine(42.5764228403785, 22.14892077680068, 39.70739165167375, 16.41085839939117)
+SketchLine_2 = Sketch_1.addLine(39.70739165167375, 16.41085839939117, 43.03546783057126, 12.04993099255995)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(28.57555063949931, 12.96802097294547, 15.72229091410204, 12.96802097294547)
+SketchLine_4 = Sketch_1.addLine(15.72229091410204, 12.96802097294547, 15.72229091410204, 21.46035329151154)
+SketchLine_5 = Sketch_1.addLine(15.72229091410204, 21.46035329151154, 28.57555063949931, 21.46035329151154)
+SketchLine_6 = Sketch_1.addLine(28.57555063949931, 21.46035329151154, 28.57555063949931, 12.96802097294547)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchLine_7 = Sketch_1.addLine(-10.67279602198167, 14.78814178154371, -28.34602814440294, 14.78814178154371)
+SketchLine_8 = Sketch_1.addLine(-28.34602814440294, 14.78814178154371, -28.34602814440294, 25.13271321305362)
+SketchLine_9 = Sketch_1.addLine(-28.34602814440294, 25.13271321305362, -10.67279602198167, 25.13271321305362)
+SketchLine_10 = Sketch_1.addLine(-10.67279602198167, 25.13271321305362, -10.67279602198167, 14.78814178154371)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_10.endPoint(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint())
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_7.result())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_9.result())
+SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(-17.67323212242127, 25.13271321305362, -21.80463703415611, 14.78814178154371)
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchLine_11.startPoint(), SketchLine_9.result())
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_11.endPoint(), SketchLine_7.result())
+model.do()
+Vertex_1 = model.addVertex(Part_1_doc, [model.selection("VERTEX", "Sketch_1/SketchArc_1")], False)
+Edge_1 = model.addEdge(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchArc_1_2")], False)
+Wire_1 = model.addWire(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2")], False)
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_6r-SketchLine_5r-SketchLine_4r-SketchLine_3r")])
+Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_10r-SketchLine_9r-SketchLine_11f-SketchLine_7r"), model.selection("FACE", "Sketch_1/Face-SketchLine_11r-SketchLine_9r-SketchLine_8r-SketchLine_7r")])
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 50)
+Box_2 = model.addBox(Part_1_doc, 10, 10, 10)
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "Box_2_1"), model.selection("SOLID", "Cylinder_1_1")])
+Box_3 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_2 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_3_1")], model.selection("EDGE", "PartSet/OY"), 50)
+AngularCopy_1 = model.addMultiRotation(Part_1_doc, [model.selection("SOLID", "Translation_2_1")], model.selection("EDGE", "PartSet/OZ"), 30, 3)
+Compound_1_objects = [model.selection("VERTEX", "Vertex_1_1"), model.selection("EDGE", "Edge_1_1"), model.selection("WIRE", "Wire_1_1"), model.selection("FACE", "Face_1_1"), model.selection("SHELL", "Shell_1_1"), model.selection("SOLID", "Translation_1_1"), model.selection("COMPSOLID", "Partition_1_1"), model.selection("COMPOUND", "AngularCopy_1_1")]
+Compound_1 = model.addCompound(Part_1_doc, Compound_1_objects)
+model.end()
+
+# collect reference data
+Z_SHIFT = 10
+REFERENCE = []
+TOLERANCE = 1.e-7
+for ind in range(0, Compound_1.result().numberOfSubs()):
+    p = Compound_1.result().subResult(ind).resultSubShapePair()[1].middlePoint()
+    p.setZ(p.z() + Z_SHIFT)
+    REFERENCE.append(p)
+
+index = 0
+
+model.begin()
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("VERTEX", "Compound_1_1_1")], model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [1])
+model.testResultsVolumes(Placement_1, [0])
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+index += 1
+
+Recover_1 = model.addRecover(Part_1_doc, Placement_1, [Compound_1.result()], True)
+Placement_2 = model.addPlacement(Part_1_doc, [model.selection("EDGE", "Recover_1_1_2")], model.selection("FACE", "Recover_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Recover_1_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_2, 1)
+model.testNbSubResults(Placement_2, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.EDGE, [1])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.VERTEX, [2])
+model.testResultsVolumes(Placement_2, [0])
+midPoint = Placement_2.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+index += 1
+
+Recover_2 = model.addRecover(Part_1_doc, Placement_2, [Recover_1.result()])
+Placement_3 = model.addPlacement(Part_1_doc, [model.selection("WIRE", "Recover_2_1_3")], model.selection("FACE", "Recover_2_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Recover_2_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_3, 1)
+model.testNbSubResults(Placement_3, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.EDGE, [2])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.VERTEX, [4])
+model.testResultsVolumes(Placement_3, [0])
+midPoint = Placement_3.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+index += 1
+
+Recover_3 = model.addRecover(Part_1_doc, Placement_3, [Recover_2.result()])
+Placement_4 = model.addPlacement(Part_1_doc, [model.selection("FACE", "Recover_3_1_4")], model.selection("FACE", "Recover_3_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Recover_3_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_4, 1)
+model.testNbSubResults(Placement_4, [0])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.FACE, [1])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.EDGE, [4])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.VERTEX, [8])
+model.testResultsVolumes(Placement_4, [109.154152964914914])
+midPoint = Placement_4.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+index += 1
+
+Recover_4 = model.addRecover(Part_1_doc, Placement_4, [Recover_3.result()])
+Placement_5 = model.addPlacement(Part_1_doc, [model.selection("SHELL", "Recover_4_1_5")], model.selection("FACE", "Recover_4_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Recover_4_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_5, 1)
+model.testNbSubResults(Placement_5, [0])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.FACE, [2])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.EDGE, [8])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.VERTEX, [16])
+model.testResultsVolumes(Placement_5, [182.822012116])
+midPoint = Placement_5.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+index += 1
+
+Recover_5 = model.addRecover(Part_1_doc, Placement_5, [Recover_4.result()])
+Placement_6 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "Recover_5_1_6")], model.selection("FACE", "Recover_5_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Recover_5_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_6, 1)
+model.testNbSubResults(Placement_6, [0])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.FACE, [6])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.EDGE, [24])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.VERTEX, [48])
+model.testResultsVolumes(Placement_6, [1000])
+midPoint = Placement_6.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+index += 1
+
+Recover_6 = model.addRecover(Part_1_doc, Placement_6, [Recover_5.result()])
+Placement_7 = model.addPlacement(Part_1_doc, [model.selection("COMPSOLID", "Recover_6_1_7")], model.selection("FACE", "Recover_6_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Recover_6_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_7, 1)
+model.testNbSubResults(Placement_7, [3])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.SOLID, [3])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.FACE, [17])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.EDGE, [66])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.VERTEX, [132])
+model.testResultsVolumes(Placement_7, [1589.0486226])
+midPoint = Placement_7.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+index += 1
+
+Recover_7 = model.addRecover(Part_1_doc, Placement_7, [Recover_6.result()])
+Placement_8 = model.addPlacement(Part_1_doc, [model.selection("COMPOUND", "Recover_7_1_8")], model.selection("FACE", "Recover_7_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Recover_7_1_6/Modified_Face&Box_1_1/Top"), False, False)
+model.testNbResults(Placement_8, 1)
+model.testNbSubResults(Placement_8, [3])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.SOLID, [3])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.FACE, [18])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.EDGE, [72])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.VERTEX, [144])
+model.testResultsVolumes(Placement_8, [3000])
+midPoint = Placement_8.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REFERENCE[index]) < TOLERANCE)
+
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_1.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_1.py
new file mode 100644 (file)
index 0000000..d57a603
--- /dev/null
@@ -0,0 +1,88 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "LinearCopy_2_1_1_1")], model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/To_Face"), centering = True, keepSubResults = True)
+
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [2])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [4])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [12])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [24])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [48])
+model.testResultsVolumes(Placement_1, [3141.592653589793])
+
+TOLERANCE = 1.e-7
+REF_C = GeomAPI_Pnt(20.590169943749483, -15, 10)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REF_C) < TOLERANCE)
+
+REF_SUB = [GeomAPI_Pnt(30.590169943749483, -25, 10), GeomAPI_Pnt(10, 20, 5)]
+for ind in range(0, len(REF_SUB)):
+    midPoint = Placement_1.result().subResult(ind).resultSubShapePair()[0].shape().middlePoint()
+    assert(midPoint.distance(REF_SUB[ind]) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_2.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_2.py
new file mode 100644 (file)
index 0000000..511a402
--- /dev/null
@@ -0,0 +1,88 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_2_1_1")], model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/To_Face"), reverse = False, centering = True, keepSubResults = True)
+
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [2])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [4])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [12])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [24])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [48])
+model.testResultsVolumes(Placement_1, [3141.592653589793])
+
+TOLERANCE = 1.e-7
+REF_C = GeomAPI_Pnt(20.590169943749483, -15, 10)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REF_C) < TOLERANCE)
+
+REF_SUB = [GeomAPI_Pnt(31.18033988749897, -50, 15), GeomAPI_Pnt(10, 20, 5)]
+for ind in range(0, len(REF_SUB)):
+    midPoint = Placement_1.result().subResult(ind).resultSubShapePair()[0].shape().middlePoint()
+    assert(midPoint.distance(REF_SUB[ind]) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_3.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_3.py
new file mode 100644 (file)
index 0000000..bbede85
--- /dev/null
@@ -0,0 +1,98 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "Compound_2_1_1_5"), model.selection("SOLID", "Compound_2_1_2_1")], model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/From_Face"), model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), centering = False, keepSubResults = True)
+
+model.end()
+
+# selection of a compound part is prohibited
+assert(Placement_1.feature().error() != "")
+
+# change the placement arguments
+model.begin()
+Placement_1.setObjects([model.selection("COMPSOLID", "Compound_2_1_1"), model.selection("SOLID", "Compound_2_1_2_1")])
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [3])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [10])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [47])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [162])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [324])
+model.testResultsVolumes(Placement_1, [14319.996])
+
+TOLERANCE = 1.e-7
+REF_C = GeomAPI_Pnt(10.29508497187, -50, 10)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REF_C) < TOLERANCE)
+
+REF_SUB = [GeomAPI_Pnt(4.566865353179923, -50, 15),
+           GeomAPI_Pnt(10.29508497, -32.16838976, 10),
+           GeomAPI_Pnt(-20.59016994, -85.663220479, 5)]
+for ind in range(0, len(REF_SUB)):
+    midPoint = Placement_1.result().subResult(ind).resultSubShapePair()[0].shape().middlePoint()
+    assert(midPoint.distance(REF_SUB[ind]) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_4.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_4.py
new file mode 100644 (file)
index 0000000..d8e9eec
--- /dev/null
@@ -0,0 +1,90 @@
+# 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], model.selection("EDGE", "PartSet/OX"), 20, 2)
+LinearCopy_2 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPOUND", "LinearCopy_1_1")], model.selection("EDGE", "PartSet/OY"), 20, 2)
+Sketch_1 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchEllipse_1 = Sketch_1.addEllipse(11.18033988749894, -50, 22.36067977499789, -50, 10)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_3 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_3.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_3).startPoint(), SketchAPI_Point(SketchPoint_3).coordinates(), 50, True)
+SketchCircle_1 = Sketch_1.addCircle(41.18033988749897, -50, 5)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchLine_1.result())
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchEllipse_1.majorAxisPositive(), SketchCircle_1.center(), 15, True)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 5)
+SketchMultiRotation_1 = Sketch_1.addRotation([SketchEllipse_1.result(), SketchCircle_1.results()[1]], SketchAPI_Point(SketchPoint_3).coordinates(), 360, 3, True)
+[SketchEllipse_2, SketchEllipse_3, SketchCircle_2, SketchCircle_3] = SketchMultiRotation_1.rotated()
+model.do()
+Sketch_1.changeFacesOrder([[SketchEllipse_1.result(), SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchCircle_1.results()[1]],
+                           [SketchEllipse_2.result(), SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_3.result(), SketchEllipse_2.result(), SketchEllipse_1.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_2.result(), SketchEllipse_3.result()],
+                           [SketchEllipse_1.result(), SketchEllipse_3.result(), SketchEllipse_2.result()],
+                           [SketchEllipse_2.result(), SketchEllipse_3.result(), SketchEllipse_3.result(), SketchEllipse_1.result()],
+                           [SketchCircle_2.results()[1]],
+                           [SketchCircle_3.results()[1]]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Compound_1 = model.addCompound(Part_1_doc, [model.selection("SOLID", "Extrusion_1_2"), model.selection("SOLID", "Extrusion_1_3")])
+Compound_2_objects = [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "Compound_1_1"), model.selection("SOLID", "Extrusion_1_4")]
+Compound_2 = model.addCompound(Part_1_doc, Compound_2_objects)
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("COMPSOLID", "Compound_2_1_1"), model.selection("SOLID", "Compound_2_1_3")], model.selection("FACE", "Compound_2_1_2_1/Modified_Face&Extrusion_1_2/From_Face"), model.selection("FACE", "LinearCopy_2_1_1_1/MF:Translated&Cylinder_1_1/Face_2"), keepSubResults = True)
+
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [3])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [10])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [47])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [162])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [324])
+model.testResultsVolumes(Placement_1, [14319.996])
+
+TOLERANCE = 1.e-7
+REF_C = GeomAPI_Pnt(10.29508497187, -50, 10)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(REF_C) < TOLERANCE)
+
+REF_SUB = [GeomAPI_Pnt(4.566865353179923, -50, 15),
+           GeomAPI_Pnt(10.29508497, -32.16838976, 5),
+           GeomAPI_Pnt(-20.59016994, -85.663220479, 15)]
+for ind in range(0, len(REF_SUB)):
+    midPoint = Placement_1.result().subResult(ind).resultSubShapePair()[0].shape().middlePoint()
+    assert(midPoint.distance(REF_SUB[ind]) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_5.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_5.py
new file mode 100644 (file)
index 0000000..86ec4d2
--- /dev/null
@@ -0,0 +1,187 @@
+# 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 salome.shaper import model
+from GeomAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchPoint_1 = Sketch_1.addPoint(44.29784155360136, 17.09942588468031)
+SketchArc_1 = Sketch_1.addArc(44.29784155360136, 17.09942588468031, 47.1668727423061, 12.27945348765633, 45.38299232715926, 22.60269052200972, False)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchArc_1.center())
+SketchLine_1 = Sketch_1.addLine(42.5764228403785, 22.14892077680068, 39.70739165167375, 16.41085839939117)
+SketchLine_2 = Sketch_1.addLine(39.70739165167375, 16.41085839939117, 43.03546783057126, 12.04993099255995)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(28.57555063949931, 12.96802097294547, 15.72229091410204, 12.96802097294547)
+SketchLine_4 = Sketch_1.addLine(15.72229091410204, 12.96802097294547, 15.72229091410204, 21.46035329151154)
+SketchLine_5 = Sketch_1.addLine(15.72229091410204, 21.46035329151154, 28.57555063949931, 21.46035329151154)
+SketchLine_6 = Sketch_1.addLine(28.57555063949931, 21.46035329151154, 28.57555063949931, 12.96802097294547)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchLine_7 = Sketch_1.addLine(-10.67279602198167, 14.78814178154371, -28.34602814440294, 14.78814178154371)
+SketchLine_8 = Sketch_1.addLine(-28.34602814440294, 14.78814178154371, -28.34602814440294, 25.13271321305362)
+SketchLine_9 = Sketch_1.addLine(-28.34602814440294, 25.13271321305362, -10.67279602198167, 25.13271321305362)
+SketchLine_10 = Sketch_1.addLine(-10.67279602198167, 25.13271321305362, -10.67279602198167, 14.78814178154371)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_10.endPoint(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint())
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_7.result())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_9.result())
+SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(-17.67323212242127, 25.13271321305362, -21.80463703415611, 14.78814178154371)
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchLine_11.startPoint(), SketchLine_9.result())
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_11.endPoint(), SketchLine_7.result())
+model.do()
+Vertex_1 = model.addVertex(Part_1_doc, [model.selection("VERTEX", "Sketch_1/SketchArc_1")], False)
+Edge_1 = model.addEdge(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchArc_1_2")], False)
+Wire_1 = model.addWire(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2")], False)
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_6r-SketchLine_5r-SketchLine_4r-SketchLine_3r")])
+Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_10r-SketchLine_9r-SketchLine_11f-SketchLine_7r"), model.selection("FACE", "Sketch_1/Face-SketchLine_11r-SketchLine_9r-SketchLine_8r-SketchLine_7r")])
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 50)
+Box_2 = model.addBox(Part_1_doc, 10, 10, 10)
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "Box_2_1"), model.selection("SOLID", "Cylinder_1_1")], keepSubResults = True)
+Box_3 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_2 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_3_1")], model.selection("EDGE", "PartSet/OY"), 50)
+AngularCopy_1 = model.addMultiRotation(Part_1_doc, [model.selection("SOLID", "Translation_2_1")], model.selection("EDGE", "PartSet/OZ"), 30, 3)
+model.end()
+
+Z_SHIFT = 10
+TOLERANCE = 1.e-7
+
+model.begin()
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("VERTEX", "Vertex_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_1, 1)
+model.testNbSubResults(Placement_1, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.EDGE, [0])
+model.testNbSubShapes(Placement_1, GeomAPI_Shape.VERTEX, [1])
+model.testResultsVolumes(Placement_1, [0])
+refPoint = Vertex_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_1.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_2 = model.addPlacement(Part_1_doc, [model.selection("EDGE", "Edge_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_2, 1)
+model.testNbSubResults(Placement_2, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.EDGE, [1])
+model.testNbSubShapes(Placement_2, GeomAPI_Shape.VERTEX, [2])
+model.testResultsVolumes(Placement_2, [0])
+refPoint = Edge_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_2.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_3 = model.addPlacement(Part_1_doc, [model.selection("WIRE", "Wire_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_3, 1)
+model.testNbSubResults(Placement_3, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.FACE, [0])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.EDGE, [2])
+model.testNbSubShapes(Placement_3, GeomAPI_Shape.VERTEX, [4])
+model.testResultsVolumes(Placement_3, [0])
+refPoint = Wire_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_3.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_4 = model.addPlacement(Part_1_doc, [model.selection("FACE", "Face_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_4, 1)
+model.testNbSubResults(Placement_4, [0])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.FACE, [1])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.EDGE, [4])
+model.testNbSubShapes(Placement_4, GeomAPI_Shape.VERTEX, [8])
+model.testResultsVolumes(Placement_4, [109.154152964914914])
+refPoint = Face_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_4.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_5 = model.addPlacement(Part_1_doc, [model.selection("SHELL", "Shell_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_5, 1)
+model.testNbSubResults(Placement_5, [0])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.SOLID, [0])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.FACE, [2])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.EDGE, [8])
+model.testNbSubShapes(Placement_5, GeomAPI_Shape.VERTEX, [16])
+model.testResultsVolumes(Placement_5, [182.822012116])
+refPoint = Shell_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_5.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_6 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "Translation_1_1")], model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Bottom"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_1_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_6, 1)
+model.testNbSubResults(Placement_6, [0])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.FACE, [6])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.EDGE, [24])
+model.testNbSubShapes(Placement_6, GeomAPI_Shape.VERTEX, [48])
+model.testResultsVolumes(Placement_6, [1000])
+refPoint = Translation_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_6.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_7 = model.addPlacement(Part_1_doc, [model.selection("COMPSOLID", "Partition_1_1")], model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Bottom"), model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_7, 1)
+model.testNbSubResults(Placement_7, [3])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.SOLID, [3])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.FACE, [17])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.EDGE, [66])
+model.testNbSubShapes(Placement_7, GeomAPI_Shape.VERTEX, [132])
+model.testResultsVolumes(Placement_7, [1589.0486226])
+refPoint = Partition_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_7.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+Placement_8 = model.addPlacement(Part_1_doc, [model.selection("COMPOUND", "AngularCopy_1_1")],model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Bottom"), model.selection("FACE", "AngularCopy_1_1_1/MF:Rotated&Box_3_1/Top"), keepSubResults = True)
+model.testNbResults(Placement_8, 1)
+model.testNbSubResults(Placement_8, [3])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.SOLID, [3])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.FACE, [18])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.EDGE, [72])
+model.testNbSubShapes(Placement_8, GeomAPI_Shape.VERTEX, [144])
+model.testResultsVolumes(Placement_8, [3000])
+refPoint = AngularCopy_1.defaultResult().shape().middlePoint()
+refPoint.setZ(refPoint.z() + Z_SHIFT)
+midPoint = Placement_8.defaultResult().shape().middlePoint()
+assert(midPoint.distance(refPoint) < TOLERANCE)
+
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_6.py b/src/FeaturesPlugin/Test/TestPlacement_MultiLevelCompound_v95_6.py
new file mode 100644 (file)
index 0000000..472756a
--- /dev/null
@@ -0,0 +1,131 @@
+# 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 salome.shaper import model
+from GeomAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchPoint_1 = Sketch_1.addPoint(44.29784155360136, 17.09942588468031)
+SketchArc_1 = Sketch_1.addArc(44.29784155360136, 17.09942588468031, 47.1668727423061, 12.27945348765633, 45.38299232715926, 22.60269052200972, False)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchArc_1.center())
+SketchLine_1 = Sketch_1.addLine(42.5764228403785, 22.14892077680068, 39.70739165167375, 16.41085839939117)
+SketchLine_2 = Sketch_1.addLine(39.70739165167375, 16.41085839939117, 43.03546783057126, 12.04993099255995)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(28.57555063949931, 12.96802097294547, 15.72229091410204, 12.96802097294547)
+SketchLine_4 = Sketch_1.addLine(15.72229091410204, 12.96802097294547, 15.72229091410204, 21.46035329151154)
+SketchLine_5 = Sketch_1.addLine(15.72229091410204, 21.46035329151154, 28.57555063949931, 21.46035329151154)
+SketchLine_6 = Sketch_1.addLine(28.57555063949931, 21.46035329151154, 28.57555063949931, 12.96802097294547)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_6.result())
+SketchLine_7 = Sketch_1.addLine(-10.67279602198167, 14.78814178154371, -28.34602814440294, 14.78814178154371)
+SketchLine_8 = Sketch_1.addLine(-28.34602814440294, 14.78814178154371, -28.34602814440294, 25.13271321305362)
+SketchLine_9 = Sketch_1.addLine(-28.34602814440294, 25.13271321305362, -10.67279602198167, 25.13271321305362)
+SketchLine_10 = Sketch_1.addLine(-10.67279602198167, 25.13271321305362, -10.67279602198167, 14.78814178154371)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_10.endPoint(), SketchLine_7.startPoint())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint())
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_7.result())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_9.result())
+SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(-17.67323212242127, 25.13271321305362, -21.80463703415611, 14.78814178154371)
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchLine_11.startPoint(), SketchLine_9.result())
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_11.endPoint(), SketchLine_7.result())
+model.do()
+Vertex_1 = model.addVertex(Part_1_doc, [model.selection("VERTEX", "Sketch_1/SketchArc_1")], False)
+Edge_1 = model.addEdge(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchArc_1_2")], False)
+Wire_1 = model.addWire(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchLine_1"), model.selection("EDGE", "Sketch_1/SketchLine_2")], False)
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_6r-SketchLine_5r-SketchLine_4r-SketchLine_3r")])
+Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_10r-SketchLine_9r-SketchLine_11f-SketchLine_7r"), model.selection("FACE", "Sketch_1/Face-SketchLine_11r-SketchLine_9r-SketchLine_8r-SketchLine_7r")])
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 50)
+Box_2 = model.addBox(Part_1_doc, 10, 10, 10)
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "Box_2_1"), model.selection("SOLID", "Cylinder_1_1")], keepSubResults = True)
+Box_3 = model.addBox(Part_1_doc, 10, 10, 10)
+Translation_2 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_3_1")], model.selection("EDGE", "PartSet/OY"), 50)
+AngularCopy_1 = model.addMultiRotation(Part_1_doc, [model.selection("SOLID", "Translation_2_1")], model.selection("EDGE", "PartSet/OZ"), 30, 3)
+Compound_1_objects = [model.selection("VERTEX", "Vertex_1_1"), model.selection("EDGE", "Edge_1_1"), model.selection("WIRE", "Wire_1_1"), model.selection("FACE", "Face_1_1"), model.selection("SHELL", "Shell_1_1"), model.selection("SOLID", "Translation_1_1"), model.selection("COMPSOLID", "Partition_1_1"), model.selection("COMPOUND", "AngularCopy_1_1")]
+Compound_1 = model.addCompound(Part_1_doc, Compound_1_objects)
+model.end()
+
+# collect reference data
+Z_SHIFT = 10
+REFERENCE = []
+TOLERANCE = 1.e-7
+for ind in range(0, Compound_1.result().numberOfSubs()):
+    p = Compound_1.result().subResult(ind).resultSubShapePair()[1].middlePoint()
+    REFERENCE.append(p)
+
+def assertResult(thePlacement, theNbMoved):
+    model.testNbResults(thePlacement, 1)
+    model.testNbSubResults(thePlacement, [8])
+    model.testNbSubShapes(thePlacement, GeomAPI_Shape.SOLID, [7])
+    model.testNbSubShapes(thePlacement, GeomAPI_Shape.FACE, [44])
+    model.testNbSubShapes(thePlacement, GeomAPI_Shape.EDGE, [177])
+    model.testNbSubShapes(thePlacement, GeomAPI_Shape.VERTEX, [355])
+    model.testResultsVolumes(thePlacement, [5589.0486226])
+
+    for ind in range(0, thePlacement.result().numberOfSubs()):
+        ref = GeomAPI_Pnt(REFERENCE[ind].x(), REFERENCE[ind].y(), REFERENCE[ind].z())
+        if ind < theNbMoved:
+            ref.setZ(ref.z() + Z_SHIFT)
+        midPoint = thePlacement.result().subResult(ind).resultSubShapePair()[0].shape().middlePoint()
+        assert(midPoint.distance(ref) < TOLERANCE), "Sub-result {}; actual ({}, {}, {}) != expected ({}, {}, {})".format(ind, midPoint.x(), midPoint.y(), midPoint.z(), ref.x(), ref.y(), ref.z())
+
+
+model.begin()
+Placement_1 = model.addPlacement(Part_1_doc, [model.selection("VERTEX", "Compound_1_1_1")], model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_1, 1)
+
+Placement_2 = model.addPlacement(Part_1_doc, [model.selection("EDGE", "Placement_1_1_2")], model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_2, 2)
+
+Placement_3 = model.addPlacement(Part_1_doc, [model.selection("WIRE", "Placement_2_1_3")], model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_3, 3)
+
+Placement_4 = model.addPlacement(Part_1_doc, [model.selection("FACE", "Placement_3_1_4")], model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_4, 4)
+
+Placement_5 = model.addPlacement(Part_1_doc, [model.selection("SHELL", "Placement_4_1_5")], model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_5, 5)
+
+Placement_6 = model.addPlacement(Part_1_doc, [model.selection("SOLID", "Placement_5_1_6")], model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Bottom"), model.selection("FACE", "Compound_1_1_6/Modified_Face&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_6, 6)
+
+Placement_7 = model.addPlacement(Part_1_doc, [model.selection("COMPSOLID", "Placement_6_1_7")], model.selection("FACE", "Placement_6_1_6/MF:Placed&Box_1_1/Bottom"), model.selection("FACE", "Placement_6_1_6/MF:Placed&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_7, 7)
+
+Placement_8 = model.addPlacement(Part_1_doc, [model.selection("COMPOUND", "Placement_7_1_8")], model.selection("FACE", "Placement_6_1_6/MF:Placed&Box_1_1/Bottom"), model.selection("FACE", "Placement_6_1_6/MF:Placed&Box_1_1/Top"), keepSubResults = True)
+assertResult(Placement_8, 8)
+
+model.end()
+
+assert(model.checkPythonDump())
index 87d5c3127e0256aa44e5dc9c00621e488ceb1572..cac15eabe4d78435489629b17e9fae0374b55e51 100644 (file)
@@ -48,16 +48,13 @@ void GeomAPI_ShapeHierarchy::addParent(const GeomShapePtr& theShape,
 GeomShapePtr GeomAPI_ShapeHierarchy::parent(const GeomShapePtr& theShape,
                                             bool theMarkProcessed)
 {
-  MapShapeToParent::const_iterator aFound = myParent.find(theShape);
+  MapShapeToShape::const_iterator aFound = myParent.find(theShape);
   GeomShapePtr aParent;
   if (aFound != myParent.end()) {
     aParent = aFound->second;
     if (theMarkProcessed) {
       // mark the parent and all its subs as processed by Boolean algorithm
       markProcessed(aParent);
-      const ListOfShape& aSubs = mySubshapes[myParentIndices[aParent]].second;
-      for (ListOfShape::const_iterator anIt = aSubs.begin(); anIt != aSubs.end(); ++anIt)
-        markProcessed(*anIt);
     }
   }
   return aParent;
@@ -66,6 +63,13 @@ GeomShapePtr GeomAPI_ShapeHierarchy::parent(const GeomShapePtr& theShape,
 void GeomAPI_ShapeHierarchy::markProcessed(const GeomShapePtr& theShape)
 {
   myProcessedObjects.insert(theShape);
+  // mark sub-shapes of the compound as processed too
+  MapShapeToIndex::iterator aFoundInd = myParentIndices.find(theShape);
+  if (aFoundInd != myParentIndices.end()) {
+    const ListOfShape& aSubs = mySubshapes[aFoundInd->second].second;
+    for (ListOfShape::const_iterator anIt = aSubs.begin(); anIt != aSubs.end(); ++anIt)
+      markProcessed(*anIt);
+  }
 }
 
 void GeomAPI_ShapeHierarchy::markProcessed(const ListOfShape& theShapes)
@@ -74,6 +78,12 @@ void GeomAPI_ShapeHierarchy::markProcessed(const ListOfShape& theShapes)
     markProcessed(*anIt);
 }
 
+void GeomAPI_ShapeHierarchy::markModified(const GeomShapePtr& theSource,
+                                          const GeomShapePtr& theModified)
+{
+  myModifiedObjects[theSource] = theModified;
+}
+
 void GeomAPI_ShapeHierarchy::objectsByType(
     ListOfShape& theShapesByType,
     ListOfShape& theOtherShapes,
@@ -127,6 +137,40 @@ bool GeomAPI_ShapeHierarchy::empty() const
   return myObjects.empty();
 }
 
+void GeomAPI_ShapeHierarchy::topLevelObjects(ListOfShape& theDestination) const
+{
+  GeomAPI_ShapeHierarchy* aThis = const_cast<GeomAPI_ShapeHierarchy*>(this);
+  SetOfShape aProcessed = myProcessedObjects;
+  aThis->myProcessedObjects.clear();
+
+  iterator anIt = aThis->begin(), aEnd = aThis->end();
+  for (; anIt != aEnd; ++anIt) {
+    GeomShapePtr aShape = *anIt;
+    GeomShapePtr aParent = aThis->parent(aShape);
+    GeomShapePtr aRoot = aParent;
+    while (aParent) {
+      aParent = aThis->parent(aRoot);
+      if (aParent)
+        aRoot = aParent;
+    }
+
+    if (aRoot) {
+      // compose a compund with the modified shapes
+      aShape = collectSubs(aRoot, SetOfShape(), myModifiedObjects);
+    }
+    else {
+      // check the current shape was modified
+      MapShapeToShape::const_iterator aFound = myModifiedObjects.find(aShape);
+      if (aFound != myModifiedObjects.end())
+        aShape = aFound->second;
+    }
+
+    theDestination.push_back(aShape);
+  }
+
+  aThis->myProcessedObjects = aProcessed;
+}
+
 void GeomAPI_ShapeHierarchy::compoundsOfUnusedObjects(
     ListOfShape& theDestination) const
 {
@@ -135,11 +179,11 @@ void GeomAPI_ShapeHierarchy::compoundsOfUnusedObjects(
 
   for (std::vector<ShapeAndSubshapes>::const_iterator anIt = mySubshapes.begin();
        anIt != mySubshapes.end(); ++anIt) {
-    MapShapeToParent::const_iterator aParent = myParent.find(anIt->first);
+    MapShapeToShape::const_iterator aParent = myParent.find(anIt->first);
     if ((aParent == myParent.end() || !aParent->second) &&
          anIt->first->shapeType() ==  GeomAPI_Shape::COMPOUND) {
       // this is a top-level compound
-      GeomShapePtr aCompound = collectUnusedSubs(anIt->first, aUsedObjects);
+      GeomShapePtr aCompound = collectSubs(anIt->first, aUsedObjects);
       // add to destination non-empty compounds only
       if (aCompound)
         theDestination.push_back(aCompound);
@@ -147,28 +191,34 @@ void GeomAPI_ShapeHierarchy::compoundsOfUnusedObjects(
   }
 }
 
-static void addSubShape(GeomShapePtr theTarget, GeomShapePtr theSub)
+static void addSubShape(GeomShapePtr theTarget, GeomShapePtr theSub,
+    const std::map<GeomShapePtr, GeomShapePtr, GeomAPI_Shape::Comparator>& theModified)
 {
   if (!theTarget.get() || !theSub.get())
     return;
 
   TopoDS_Shape* aShape = theTarget->implPtr<TopoDS_Shape>();
-  const TopoDS_Shape& aShapeToAdd = theSub->impl<TopoDS_Shape>();
+
+  std::map<GeomShapePtr, GeomShapePtr, GeomAPI_Shape::Comparator>::const_iterator
+    aFound = theModified.find(theSub);
+  const TopoDS_Shape& aShapeToAdd =
+      (aFound == theModified.end() ? theSub : aFound->second)->impl<TopoDS_Shape>();
 
   static BRep_Builder aBuilder;
   aBuilder.Add(*aShape, aShapeToAdd);
 }
 
-GeomShapePtr GeomAPI_ShapeHierarchy::collectUnusedSubs(
+GeomShapePtr GeomAPI_ShapeHierarchy::collectSubs(
     GeomShapePtr theTopLevelCompound,
-    const SetOfShape& theUsed) const
+    const SetOfShape& theExcluded,
+    const MapShapeToShape& theModified) const
 {
   GeomShapePtr aResult = theTopLevelCompound->emptyCopied();
   bool isResultEmpty = true;
 
   for (GeomAPI_ShapeIterator aSub(theTopLevelCompound); aSub.more(); aSub.next()) {
     GeomShapePtr aCurrent = aSub.current();
-    if (theUsed.find(aCurrent) != theUsed.end())
+    if (theExcluded.find(aCurrent) != theExcluded.end())
       continue; // already used
 
     MapShapeToIndex::const_iterator aFoundIndex = myParentIndices.find(aCurrent);
@@ -178,17 +228,17 @@ GeomShapePtr GeomAPI_ShapeHierarchy::collectUnusedSubs(
       // check compsolid is fully unused in the Boolean operation
       if (aCurrent->shapeType() == GeomAPI_Shape::COMPSOLID) {
         for (GeomAPI_ShapeIterator anIt(aCurrent); isAddShape && anIt.more(); anIt.next())
-          isAddShape = theUsed.find(anIt.current()) == theUsed.end();
+          isAddShape = theExcluded.find(anIt.current()) == theExcluded.end();
       }
 
       if (isAddShape) { // low-level shape, add it
-        addSubShape(aResult, aCurrent);
+        addSubShape(aResult, aCurrent, theModified);
         isResultEmpty = false;
       }
     } else {
-      GeomShapePtr aCompound = collectUnusedSubs(aCurrent, theUsed);
+      GeomShapePtr aCompound = collectSubs(aCurrent, theExcluded, theModified);
       if (aCompound) {
-        addSubShape(aResult, aCompound);
+        addSubShape(aResult, aCompound, theModified);
         isResultEmpty = false;
       }
     }
index 538b826af34553bd3f2b4f1030ea3e094dc56f14..b1492fe8cb002c60900000e3e84aabaec55106a6 100644 (file)
 class GeomAPI_ShapeHierarchy
 {
   typedef std::pair<GeomShapePtr, ListOfShape> ShapeAndSubshapes;
-  typedef std::map<GeomShapePtr, GeomShapePtr, GeomAPI_Shape::Comparator> MapShapeToParent;
+  typedef std::map<GeomShapePtr, GeomShapePtr, GeomAPI_Shape::Comparator> MapShapeToShape;
   typedef std::map<GeomShapePtr, size_t, GeomAPI_Shape::Comparator> MapShapeToIndex;
   typedef std::set<GeomShapePtr, GeomAPI_Shape::Comparator> SetOfShape;
 
   ListOfShape myObjects; ///< list of objects of some operation
-  MapShapeToParent myParent; ///< refer a shape to compound/compsolid containing it
+  MapShapeToShape myParent; ///< refer a shape to compound/compsolid containing it
   /// indices of compounds/compsolids to keep the order of parent shapes
   /// corresponding to the order of objects
   MapShapeToIndex  myParentIndices;
@@ -46,6 +46,7 @@ class GeomAPI_ShapeHierarchy
   std::vector<ShapeAndSubshapes> mySubshapes;
 
   SetOfShape myProcessedObjects;
+  MapShapeToShape myModifiedObjects;
 
 public:
   /// Add an object of the operation (low-level shape in the hierarchy)
@@ -65,6 +66,9 @@ public:
   /// Mark list ofshapes as already processed
   GEOMAPI_EXPORT void markProcessed(const ListOfShape& theShapes);
 
+  /// Mark the shape as modified and store its image
+  GEOMAPI_EXPORT void markModified(const GeomShapePtr& theSource, const GeomShapePtr& theModified);
+
   /// Split compound/compsolid shape for subshapes selected for operation and the others.
   GEOMAPI_EXPORT void splitCompound(const GeomShapePtr& theCompShape,
                                     ListOfShape& theUsed,
@@ -73,6 +77,9 @@ public:
   /// Generates the list of top-level compounds, which exclude the objects of operation.
   GEOMAPI_EXPORT void compoundsOfUnusedObjects(ListOfShape& theDestination) const;
 
+  /// Generates the list of top-level compounds, with modified objects of operation.
+  GEOMAPI_EXPORT void topLevelObjects(ListOfShape& theDestination) const;
+
   /// Return \c true if there is no object in hierarchy
   GEOMAPI_EXPORT bool empty() const;
 
@@ -84,8 +91,11 @@ public:
       const GeomAPI_Shape::ShapeType theMaxType = GeomAPI_Shape::SHAPE) const;
 
 private:
-  GeomShapePtr collectUnusedSubs(const GeomShapePtr theTopLevelCompound,
-                                 const SetOfShape& theUsed) const;
+  /// Collect subs of a top-level compound, excluding the set of given objects
+  /// and substitute the shapes which were modified
+  GeomShapePtr collectSubs(const GeomShapePtr theTopLevelCompound,
+                           const SetOfShape& theExcluded = SetOfShape(),
+                           const MapShapeToShape& theModified = MapShapeToShape()) const;
 
 public:
   class iterator : public std::iterator<std::forward_iterator_tag, GeomShapePtr>
index f80fd84f4b138bd23cb8294939980c48936b9bc1..0f7faf296f8daab4368ef1cf1407014320dbb16a 100644 (file)
@@ -130,7 +130,7 @@ def testResultsVolumes(theFeature, theExpectedResultsVolumes, theNbSignificantDi
     aResultVolumeStr = "{:0.27f}".format(aResultVolume).lstrip("0").lstrip(".").lstrip("0")
     anExpectedResultVolume = theExpectedResultsVolumes[anIndex]
     anExpectedResultVolumeStr = "{:0.27f}".format(anExpectedResultVolume).lstrip("0").lstrip(".").lstrip("0")
-    assert math.fabs(aResultVolume - anExpectedResultVolume) < aTolerance * math.fabs(anExpectedResultVolume), "Volume of result[{}]: {:0.27f}. Expected: {:0.27f}. The first {} significant digits not equal.".format(anIndex, aResultVolume, anExpectedResultVolume, theNbSignificantDigits)
+    assert math.fabs(aResultVolume - anExpectedResultVolume) <= aTolerance * math.fabs(anExpectedResultVolume), "Volume of result[{}]: {:0.27f}. Expected: {:0.27f}. The first {} significant digits not equal.".format(anIndex, aResultVolume, anExpectedResultVolume, theNbSignificantDigits)
 
 def testHaveNamingFaces(theFeature, theModel, thePartDoc) :
   """ Tests if all faces of result have a name