Salome HOME
Merge branch 'master' into V9_3_BR V9_3_0rc1
authorvsr <vsr@opencascade.com>
Wed, 10 Apr 2019 13:43:22 +0000 (16:43 +0300)
committervsr <vsr@opencascade.com>
Wed, 10 Apr 2019 13:43:22 +0000 (16:43 +0300)
14 files changed:
src/BuildPlugin/Test/TestCompSolid.py
src/BuildPlugin/Test/TestFace.py
src/BuildPlugin/doc/faceFeature.rst
src/BuildPlugin/face_widget.xml
src/BuildPlugin/plugin-Build.xml
src/FeaturesPlugin/Test/TestFusionFaces.py
src/FeaturesPlugin/Test/TestFusionFaces2697.py
src/FeaturesPlugin/Test/TestRecover.py
src/FeaturesPlugin/Test/TestRemoveSubShapes3.py
src/GeomAPI/Test/TestCone.py
src/Model/Model_AttributeSelection.cpp
src/Model/Model_AttributeValidator.cpp
src/ModelAPI/CMakeLists.txt
src/ModelAPI/Test/Test2903.py [new file with mode: 0644]

index 077834374342bc66d9d0fde0ca4538c6edeb0eba..3ebbd9121a64eafcf33ec5bf3b4b7d6b118b094c 100644 (file)
@@ -147,6 +147,12 @@ CompSolid_1 = createCompSolidStepByStep(boundaries1, expectType1)
 model.checkResult(CompSolid_1, model, 1, [2], [2], [13], [54], [108])
 model.testHaveNamingSubshapes(CompSolid_1, model, Part_1_doc)
 
+# to reuse all results, undo the solid and 3 groups creation
+model.undo()
+model.undo()
+model.undo()
+model.undo()
+
 # =============================================================================
 # Test 2. Build compsolid containing 3 solids
 # =============================================================================
index 47091eeb78da7423608842dac9987239310ea93a..4ad935ab0320f54d539af26725f8cf31b8bbb50a 100644 (file)
@@ -113,11 +113,21 @@ assert (len(aFaceFeature2.results()) > 0)
 # =============================================================================
 # Test 3. Create face from face of solid
 # =============================================================================
+aSession.startOperation()
+aCylinder = aPart.addFeature("Cylinder")
+aCylinder.string("CreationMethod").setValue("Cylinder")
+aCylinder.selection("base_point").selectSubShape("VERTEX", "PartSet/Origin")
+aCylinder.selection("axis").selectSubShape("EDGE", "PartSet/OZ")
+aCylinder.real("radius").setValue(5)
+aCylinder.real("height").setValue(10)
+aSession.finishOperation()
+aCylinderResult = aCylinder.firstResult()
+aCylinderShape = aCylinderResult.shape()
 
 aSession.startOperation()
 aFaceFeature3 = aPart.addFeature("Face")
 aBaseObjectsList = aFaceFeature3.selectionList("base_objects")
-aBaseObjectsList.append("Cylinder_1_1/Face_1", "FACE")
+aBaseObjectsList.append("Cylinder_2_1/Face_1", "FACE")
 aSession.finishOperation()
 assert (len(aFaceFeature3.results()) > 0)
 
@@ -125,6 +135,17 @@ assert (len(aFaceFeature3.results()) > 0)
 # Test 4. Verify error is reported if selection of face feature is mixed (edges and face)
 # =============================================================================
 
+aSession.startOperation()
+aCylinder = aPart.addFeature("Cylinder")
+aCylinder.string("CreationMethod").setValue("Cylinder")
+aCylinder.selection("base_point").selectSubShape("VERTEX", "PartSet/Origin")
+aCylinder.selection("axis").selectSubShape("EDGE", "PartSet/OZ")
+aCylinder.real("radius").setValue(5)
+aCylinder.real("height").setValue(10)
+aSession.finishOperation()
+aCylinderResult = aCylinder.firstResult()
+aCylinderShape = aCylinderResult.shape()
+
 aSession.startOperation()
 aFaceFeature4 = aPart.addFeature("Face")
 aBaseObjectsList = aFaceFeature4.selectionList("base_objects")
@@ -132,7 +153,7 @@ aShapeExplorer = GeomAPI_ShapeExplorer(aSketchShape, GeomAPI_Shape.EDGE)
 while aShapeExplorer.more():
     aBaseObjectsList.append(aSketchResult, aShapeExplorer.current())
     aShapeExplorer.next()
-aBaseObjectsList.append("Cylinder_1_1/Face_3", "FACE")
+aBaseObjectsList.append("Cylinder_3_1/Face_3", "FACE")
 aSession.finishOperation()
 assert (len(aFaceFeature4.results()) == 0)
 # remove failed feature
index 21fa07c55dc6274f8adeaadc611e2969e012a15c..aee91f3a12f8b16d7652d4dd282ebf808120db10 100644 (file)
@@ -24,7 +24,7 @@ The following property panel will be opened:
 .. centered::
   Create a face
   
-Select one or several faces in viewer.
+Select one or several faces in viewer. Additionally, a face can be build by a closed wire or a set of edges composing a closed wire.
 
 **Apply** button creates faces.
 
index 80aa05a46c96ccae4aa45090443792fa4bbf3ae9..a23c7cf073d40c56f26904bfde30aa9a89eeba98 100644 (file)
@@ -1,7 +1,7 @@
 <source>
   <multi_selector id="base_objects"
-                  label="Segments and wires:"
-                  tooltip="Select edges on sketch, edges or wires objects."
+                  label="Objects:"
+                  tooltip="Select edges, wires or faces."
                   type_choice="edges wires faces"
                   concealment="true">
     <validator id="BuildPlugin_ValidatorBaseForBuild" parameters="edge,wire,face"/>
index 86725380fbc204a7e12a22be8e192477164c3c6e..b46aa51b18ec308f8f0b1ec07208a4ad44ec5458 100644 (file)
@@ -21,7 +21,7 @@
                helpfile="polylineFeature.html">
         <source path="polyline_widget.xml"/>
       </feature>
-      <feature id="Face" title="Face" tooltip ="Create a face from sketch edges, edges and wires objects" icon="icons/Build/feature_face.png"
+      <feature id="Face" title="Face" tooltip ="Create a face from edges, wires and faces" icon="icons/Build/feature_face.png"
                helpfile="faceFeature.html">
         <source path="face_widget.xml"/>
       </feature>
index 8481757458c3a65868a3c2dc8c4777772ee70c66..b0adb7f320c1c76fafec998eee5b34cec8ace331 100644 (file)
@@ -32,16 +32,21 @@ Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
 Box_2 = model.addBox(Part_1_doc, 10, 10, 10)
 Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_2_1")], model.selection("EDGE", "PartSet/OX"), 10)
 
+model.do()
 Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Box_1_1/Top"), model.selection("FACE", "Translation_1_1/MF:Translated&Box_2_1/Top")])
 model.do()
 
 model.checkResult(Shell_1, model, 1, [0], [0], [2], [8], [16])
 
 FusionFaces_1 = model.addFusionFaces(Part_1_doc, model.selection("SHELL", "Shell_1_1"))
-model.do()
+model.end()
 
 model.checkResult(FusionFaces_1, model, 1, [0], [0], [1], [4], [8])
 
+model.undo() # to reuse Box_1_1 and Translation_1_1
+model.undo()
+model.begin()
+
 # =============================================================================
 # Test 2. Fusion faces for solid of 2 adjacent boxes
 # =============================================================================
@@ -63,7 +68,7 @@ Shell_2 = model.addShell(Part_1_doc, [model.selection("FACE", "Cylinder_1_1/Face
 Point_1 = model.addPoint(Part_1_doc, 0, 0, 5)
 Plane_1 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OZ"), model.selection("VERTEX", "Point_1"), True)
 
-Partition_1 = model.addPartition(Part_1_doc, [model.selection("SHELL", "Shell_2_1"), model.selection("FACE", "Plane_1")])
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SHELL", "Shell_1_1"), model.selection("FACE", "Plane_1")])
 model.do()
 
 model.checkResult(Partition_1, model, 1, [0], [0], [2], [8], [16])
index e3e84ed52ca0526c3d82e2a08b17133068d454d6..ff21477909e877ebff135b8bbeb3f04ac23d8fc5 100644 (file)
@@ -256,7 +256,9 @@ Symmetry_1.results()[1].subResult(1).setName("Symmetry_1_2_2")
 Cut_1 = model.addCut(Part_1_doc, [model.selection("SOLID", "Revolution_1_1")], [model.selection("COMPOUND", "Cut_tool")])
 Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Cut_1_1"), model.selection("COMPOUND", "Fuse_tool")])
 FusionFaces_1 = model.addFusionFaces(Part_1_doc, model.selection("SOLID", "Fuse_1_1"))
-model.end()
+model.do()
 
 # check the faces are fused
 model.checkResult(FusionFaces_1, model, 1, [0], [1], [16], [76], [152])
+
+model.end()
\ No newline at end of file
index 392d85ae7ec489d1f71fd269d98c51619bd460c1..88cdb0cdcceb961e39a432eab18ae78a79906537 100644 (file)
@@ -75,7 +75,8 @@ c3 = sk3.addCircle(0, 0, 90)
 model.do()
 big2 = model.addExtrusion(mypart, sk3.selectFace(), 110)
 
-cut2 = model.addCut(mypart, big2.results(), smallcyl.results())
+smallcyl2 = model.addExtrusion(mypart, sk2.selectFace(), 150)
+cut2 = model.addCut(mypart, big2.results(), smallcyl2.results())
 
 model.end()
 
index 87dc9185becde864a223c33ed26fca695e61c493..c159dbe564a80eb0ae2587207e019f0174e5bf36 100644 (file)
@@ -30,7 +30,8 @@ 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)
 Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Cylinder_1_1/Face_1")])
-Face_2 = model.addFace(Part_1_doc, [model.selection("FACE", "Cylinder_1_1/Face_1")])
+Cylinder_2 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Face_2 = model.addFace(Part_1_doc, [model.selection("FACE", "Cylinder_2_1/Face_1")])
 Translation_1 = model.addTranslation(Part_1_doc, [model.selection("FACE", "Face_2_1")], model.selection("EDGE", "PartSet/OZ"), 10)
 Shell_1 = model.addShell(Part_1_doc, [model.selection("FACE", "Translation_1_1"), model.selection("FACE", "Face_1_1")])
 
index a7c2d72edd2f1635dac440a7e6ed2859e1770b9f..556c9bd85887b0f9e78fc95ef365df44fad1b405 100644 (file)
@@ -259,14 +259,21 @@ checkConeSolid(Part_1_doc, Solid_1_objects, anApex, anAxis, aSemiAngle, ParamR1.
 Cone_2 = model.addCone(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 10, 5, 10)
 Cone_3 = model.addCone(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10, 20)
 Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Cone_2_1"), model.selection("SOLID", "Cone_3_1")], True)
+
+model.do()
 Solid_2_objects = [model.selection("FACE", "Fuse_1_1/Modified_Face&Cone_2_1/Face_3&Cone_3_1/Face_3"),
                    model.selection("FACE", "Fuse_1_1/Modified_Face&Cone_2_1/Face_1"),
                    model.selection("FACE", "Fuse_1_1/Modified_Face&Cone_3_1/Face_1"),
                    model.selection("FACE", "Cone_3_1/Face_2")]
 Solid_2 = model.addSolid(Part_1_doc, Solid_2_objects)
 checkNonCone(Solid_2)
+model.end()
+
+# in order to use study objects once again, undo Test7 actions
+model.undo()
 
 # Test 8. Check non-conical shell
+model.begin()
 Shell_1_objects = [model.selection("FACE", "Rotation_1_1/MF:Rotated&Cone_1_1/Face_3"),
                    model.selection("FACE", "Partition_1_1_1/Modified_Face&Cone_1_1/Face_1"),
                    model.selection("FACE", "Partition_1_1_2/Modified_Face&Cone_1_1/Face_1"),
@@ -276,13 +283,23 @@ checkNonConeShell(Shell_1)
 
 Shell_2 = model.addShell(Part_1_doc, [model.selection("FACE", "Fuse_1_1/Modified_Face&Cone_2_1/Face_1"), model.selection("FACE", "Fuse_1_1/Modified_Face&Cone_3_1/Face_1")])
 checkNonConeShell(Shell_2)
+model.end()
+
+# in order to use study objects once again, undo Test8 actions
+model.undo()
 
 # Test 9. Check error on conversion to wrong type of curve
+model.begin()
 anEdge = model.addEdge(Part_1_doc, [model.selection("EDGE", "[Partition_1_1_2/Modified_Face&Cone_1_1/Face_1][Rotation_1_1/MF:Rotated&Cone_1_1/Face_2]")])
 aShape = anEdge.result().resultSubShapePair()[0].shape()
 assert(aShape.isEdge())
 assert(aShape.edge().ellipse() is None)
 assert(aShape.edge().line() is None)
+model.end()
+
+model.undo()
+
+model.begin()
 
 anEdge = model.addEdge(Part_1_doc, [model.selection("EDGE", "[Partition_1_1_2/Modified_Face&Cone_1_1/Face_1][weak_name_2]")])
 aShape = anEdge.result().resultSubShapePair()[0].shape()
@@ -301,6 +318,4 @@ radius = 5
 cone = GeomAPI_Cone(apex, dir, semiAngle, radius)
 assert(cone.location().distance(apex) < TOLERANCE)
 
-
-
 model.end()
index e540e3e6663edc3f0f7d5f1c25663fdea320252f..3d325a6880680b19462f8bdcbeb6838fa97e7ae2 100644 (file)
@@ -1443,7 +1443,12 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
               continue;
 
             FeaturePtr aRefFeat = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRef)->owner());
+
             if (aRefFeat.get() && aRefFeat != owner() && aRefFeat->firstResult().get()) {
+              // check the reference is concealed: #2900
+              ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
+              if (!aValidators->isConcealed(aRefFeat->getKind(), (*aRef)->id()))
+                continue;
               FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
               if (!aDoc->isLaterByDep(aRefFeat, aThisFeature)) { // found better feature
                 aFoundNewContext = true;
index cef7202906378c2136bb1aebb90f1f25ae632976..ee59e6e583de1ecd7cff0bbb0454e2bbfbab9985 100644 (file)
 
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Tools.h>
+#include <ModelAPI_ResultBody.h>
+#include <ModelAPI_ResultPart.h>
 
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Point2D.h>
@@ -82,6 +86,90 @@ bool Model_AttributeValidator::isValid(const AttributePtr& theAttribute,
       theError.arg(anErrorMessage);
       return false;
     }
+  } else { // #2903 : check that concealed attribute refers to already concealed result
+    FeaturePtr aFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
+
+    std::set<ObjectPtr> alreadyProcessed; // optimization
+    if (aFeat.get() &&
+        ModelAPI_Session::get()->validators()->isConcealed(aFeat->getKind(), theAttribute->id())) {
+      std::list<std::pair<std::string, std::list<ObjectPtr> > > allRefs;
+      aFeat->data()->referencesToObjects(allRefs);
+      std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator anIter = allRefs.begin();
+      for(; anIter != allRefs.end(); anIter++) {
+        if (anIter->first == theAttribute->id()) {
+          const std::list<ObjectPtr>& aReferencedList = anIter->second;
+          std::list<ObjectPtr>::const_iterator aRefIter = aReferencedList.cbegin();
+          for(; aRefIter != aReferencedList.cend(); aRefIter++) {
+            const ObjectPtr& aReferenced = *aRefIter;
+            if (!aReferenced.get())
+              continue;
+            // get all results and feature that is referenced to see all references to them
+            FeaturePtr aReferencedFeature;
+            if (aReferenced->groupName() == ModelAPI_Feature::group()) {
+              aReferencedFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aReferenced);
+            } else {
+              aReferencedFeature = aReferenced->document()->feature(
+                std::dynamic_pointer_cast<ModelAPI_Result>(aReferenced));
+            }
+            if (alreadyProcessed.count(aReferencedFeature))
+              continue;
+            alreadyProcessed.insert(aReferencedFeature);
+            /* it takes all results, not only concealed
+            std::list<ResultPtr> aReferencedResults;
+            ModelAPI_Tools::allResults(aReferencedFeature, aReferencedResults);
+            */
+            std::list<ResultPtr> aReferencedResults;
+            ResultBodyPtr aRefBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aReferenced);
+            if (aRefBody.get()) { // take only sub-results of this result or sub-result
+              ResultBodyPtr aRoot = ModelAPI_Tools::bodyOwner(aRefBody, true);
+              if (aRoot.get()) {
+                ModelAPI_Tools::allSubs(aRoot, aReferencedResults, false);
+                aReferencedResults.push_back(aRoot);
+              } else
+                aReferencedResults.push_back(aRefBody);
+            }
+
+            std::list<ResultPtr>::iterator aRefRes = aReferencedResults.begin();
+            bool aCheckFeature = true; // the last iteration to check the feature
+            while(aRefRes != aReferencedResults.end() || aCheckFeature) {
+              ObjectPtr aRefd;
+              if (aRefRes == aReferencedResults.end()) {
+                aRefd = aReferencedFeature;
+                aCheckFeature = false;
+                if (!aReferencedFeature->results().empty() &&
+                    aReferencedFeature->firstResult()->groupName() != ModelAPI_ResultBody::group())
+                  break;
+              } else {
+                aRefd = *aRefRes;
+                if (aRefd->groupName() != ModelAPI_ResultBody::group())
+                  break;
+              }
+              if (!aRefd->data().get() || !aRefd->data()->isValid())
+                continue;
+              const std::set<AttributePtr>& aRefsToRef = aRefd->data()->refsToMe();
+              std::set<AttributePtr>::const_iterator aRR = aRefsToRef.cbegin();
+              for(; aRR != aRefsToRef.cend(); aRR++) {
+                FeaturePtr aRefFeat = std::dynamic_pointer_cast<ModelAPI_Feature>((*aRR)->owner());
+                if (!aRefFeat.get() || aRefFeat == aFeat)
+                  continue;
+                if (ModelAPI_Session::get()->validators()->isConcealed(
+                    aRefFeat->getKind(), (*aRR)->id())) {
+                  // check this feature is later than another referenced to make unit tests working
+                  //because of Test1757 and others: allow to move selection context of this to next
+                  if (aFeat->document()->isLater(aFeat, aRefFeat)) {
+                    theError = "Reference to concealed object %1";
+                    theError.arg(aRefd->data()->name());
+                    return false;
+                  }
+                }
+              }
+              if (aCheckFeature)
+                aRefRes++;
+            }
+          }
+        }
+      }
+    }
   }
   return true;
 }
index dbb19965cad7914ae46dc31101d158177bad3d68..27bf507126f6c65d5064aecd04f5de07aa58e351 100644 (file)
@@ -245,4 +245,5 @@ ADD_UNIT_TESTS(TestConstants.py
                Test2859.py
                Test2873.py
                Test2901.py
+               Test2903.py
 )
diff --git a/src/ModelAPI/Test/Test2903.py b/src/ModelAPI/Test/Test2903.py
new file mode 100644 (file)
index 0000000..b868a4f
--- /dev/null
@@ -0,0 +1,41 @@
+# Copyright (C) 2014-2019  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 ModelAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = 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)
+Sphere_1 = model.addSphere(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), 10)
+Fuse_1 = model.addFuse(Part_1_doc, [model.selection("COMPOUND", "all-in-Box_1"), model.selection("COMPOUND", "all-in-Cylinder_1")], True)
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("COMPOUND", "all-in-Fuse_1"), model.selection("COMPOUND", "all-in-Sphere_1")])
+model.do()
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Fuse_1.feature()))
+assert(aFactory.validate(Partition_1.feature()))
+# Modify Fuse to add sphere thatwas used in Partition
+Fuse_1.setMainObjects([model.selection("COMPOUND", "all-in-Box_1"), model.selection("COMPOUND", "all-in-Cylinder_1"), model.selection("COMPOUND", "all-in-Sphere_1")])
+model.end()
+# partition must become invalid because it refers to the same object as fuse
+assert(aFactory.validate(Fuse_1.feature()))
+assert(not aFactory.validate(Partition_1.feature()))