]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Merge remote-tracking branch 'origin/EDF_2019'
authorvsv <vsv@opencascade.com>
Mon, 25 Nov 2019 10:01:42 +0000 (13:01 +0300)
committervsv <vsv@opencascade.com>
Mon, 25 Nov 2019 10:01:42 +0000 (13:01 +0300)
# Conflicts:
# src/FeaturesPlugin/Test/TestBooleanFuse_MultiLevelCompound_v20190506_2.py
# src/PartSet/PartSet_WidgetSketchLabel.h
# src/XGUI/XGUI_Workshop.cpp

162 files changed:
env_linux.sh
src/CollectionPlugin/plugin-Collection.xml
src/Config/Config_FeatureMessage.cpp
src/Config/Config_FeatureMessage.h
src/Config/Config_FeatureReader.cpp
src/Config/Config_Keywords.h
src/Config/Config_WidgetAPI.h
src/ConstructionPlugin/plugin-Construction.xml
src/ExchangeAPI/ExchangeAPI_Export.cpp
src/ExchangeAPI/ExchangeAPI_Export.h
src/ExchangeAPI/ExchangeAPI_Import.cpp
src/ExchangeAPI/ExchangeAPI_Import.h
src/ExchangePlugin/CMakeLists.txt
src/ExchangePlugin/ExchangePlugin_ExportPart.cpp [new file with mode: 0644]
src/ExchangePlugin/ExchangePlugin_ExportPart.h [new file with mode: 0644]
src/ExchangePlugin/ExchangePlugin_ImportPart.cpp [new file with mode: 0644]
src/ExchangePlugin/ExchangePlugin_ImportPart.h [new file with mode: 0644]
src/ExchangePlugin/ExchangePlugin_Plugin.cpp
src/ExchangePlugin/ExchangePlugin_msg_en.ts
src/ExchangePlugin/Test/TestExportPart_Failure_1.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Failure_2.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Failure_3.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_FullPartSet.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_FullPart_1.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_FullPart_2.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_PartSet.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_1.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_2.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_3.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_4.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_5.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_6.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_7.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestExportPart_Results_8.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterCurrent_1.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterCurrent_2.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterLast_1.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterLast_2.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterLast_3.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterLast_4.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterLast_5.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_AfterLast_6.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_Construction_1.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_Construction_2.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_Construction_3.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_Construction_4.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_Multiple.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_ToEmptyPart.py [new file with mode: 0644]
src/ExchangePlugin/Test/TestImportPart_ToEmptyPartSet.py [new file with mode: 0644]
src/ExchangePlugin/icons/export_part.png [new file with mode: 0644]
src/ExchangePlugin/icons/import_part.png [new file with mode: 0644]
src/ExchangePlugin/plugin-Exchange.xml
src/FeaturesPlugin/FeaturesPlugin_BooleanFuse.cpp
src/FeaturesPlugin/FeaturesPlugin_Validators.cpp
src/FeaturesPlugin/Test/TestBooleanFuse_CompSolid_Face.py
src/FeaturesPlugin/Test/TestBooleanFuse_ErrorMsg.py
src/FeaturesPlugin/Test/TestBooleanFuse_Face_Face.py
src/FeaturesPlugin/Test/TestBooleanFuse_MultiLevelCompound_v0_3.py
src/FeaturesPlugin/Test/TestBooleanFuse_MultiLevelCompound_v20190506_2.py
src/FeaturesPlugin/Test/TestBooleanFuse_MultiLevelCompound_v20190506_3.py
src/FeaturesPlugin/Test/TestUnion.py
src/FeaturesPlugin/Test/TestUnion4CurvedFaces.py
src/FeaturesPlugin/Test/TestUnion4CurvedFaces_2.py
src/FeaturesPlugin/Test/TestUnion4Faces.py
src/FeaturesPlugin/Test/TestUnionOfUnion.py
src/FeaturesPlugin/plugin-Features.xml
src/GeomAlgoAPI/GeomAlgoAPI_UnifySameDomain.cpp
src/GeomData/GeomData_Dir.cpp
src/Model/CMakeLists.txt
src/Model/Model_Application.cpp
src/Model/Model_AttributeReference.cpp
src/Model/Model_AttributeSelection.cpp
src/Model/Model_AttributeSelection.h
src/Model/Model_Data.cpp
src/Model/Model_Data.h
src/Model/Model_Document.cpp
src/Model/Model_Document.h
src/Model/Model_Tools.cpp [new file with mode: 0644]
src/Model/Model_Tools.h [new file with mode: 0644]
src/ModelAPI/ModelAPI_Document.h
src/ModelHighAPI/ModelHighAPI_FeatureStore.cpp
src/ModuleBase/CMakeLists.txt
src/ModuleBase/ModuleBase_ChoiceCtrl.cpp
src/ModuleBase/ModuleBase_IModule.h
src/ModuleBase/ModuleBase_IPropertyPanel.cpp
src/ModuleBase/ModuleBase_IPropertyPanel.h
src/ModuleBase/ModuleBase_IWorkshop.h
src/ModuleBase/ModuleBase_ListView.cpp
src/ModuleBase/ModuleBase_ListView.h
src/ModuleBase/ModuleBase_ModelWidget.cpp
src/ModuleBase/ModuleBase_ModelWidget.h
src/ModuleBase/ModuleBase_Operation.cpp
src/ModuleBase/ModuleBase_Operation.h
src/ModuleBase/ModuleBase_OperationAction.cpp [deleted file]
src/ModuleBase/ModuleBase_OperationAction.h [deleted file]
src/ModuleBase/ModuleBase_OperationFeature.cpp
src/ModuleBase/ModuleBase_ResultPrs.cpp
src/ModuleBase/ModuleBase_ResultPrs.h
src/ModuleBase/ModuleBase_ToolBox.cpp
src/ModuleBase/ModuleBase_Tools.cpp
src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp
src/ModuleBase/ModuleBase_WidgetDoubleValue.h
src/ModuleBase/ModuleBase_WidgetFactory.cpp
src/ModuleBase/ModuleBase_WidgetIntValue.cpp
src/ModuleBase/ModuleBase_WidgetIntValue.h
src/ModuleBase/ModuleBase_WidgetLabel.cpp
src/ModuleBase/ModuleBase_WidgetLineEdit.cpp
src/ModuleBase/ModuleBase_WidgetLineEdit.h
src/ModuleBase/ModuleBase_WidgetMultiSelector.cpp
src/ModuleBase/ModuleBase_WidgetMultiSelector.h
src/ModuleBase/ModuleBase_WidgetShapeSelector.cpp
src/ModuleBase/ModuleBase_WidgetShapeSelector.h
src/ModuleBase/ModuleBase_WidgetUndoLabel.cpp [new file with mode: 0644]
src/ModuleBase/ModuleBase_WidgetUndoLabel.h [new file with mode: 0644]
src/ParametersPlugin/ParametersPlugin_WidgetParamsMgr.cpp
src/PartSet/PartSet_IconFactory.cpp
src/PartSet/PartSet_MenuMgr.cpp
src/PartSet/PartSet_Module.cpp
src/PartSet/PartSet_Module.h
src/PartSet/PartSet_SketcherMgr.cpp
src/PartSet/PartSet_SketcherMgr.h
src/PartSet/PartSet_WidgetSketchCreator.h
src/PartSet/PartSet_WidgetSketchLabel.cpp
src/PartSet/PartSet_WidgetSketchLabel.h
src/PythonAPI/model/exchange/__init__.py
src/PythonAPI/model/exchange/tools.py [new file with mode: 0644]
src/SHAPERGUI/SHAPERGUI.cpp
src/SHAPERGUI/SHAPERGUI.h
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_ConstraintDistance.cpp
src/SketchPlugin/SketchPlugin_ConstraintDistance.h
src/SketchPlugin/SketchPlugin_Line.cpp
src/SketchPlugin/SketchPlugin_Line.h
src/SketchPlugin/SketchPlugin_Sketch.cpp
src/SketchPlugin/Test/TestConstraintDistanceBehavior.py
src/SketchPlugin/Test/TestConstraintDistanceHorizontal.py
src/SketchPlugin/Test/TestConstraintDistanceHorizontalZero.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintDistanceVertical.py
src/SketchPlugin/Test/TestConstraintDistanceVerticalZero.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintDistanceZero.py [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ConstraintWrapper.cpp
src/SketchSolver/SketchSolver_ConstraintDistance.cpp
src/SketchSolver/SketchSolver_Error.h
src/SketcherPrs/SketcherPrs_LengthDimension.cpp
src/XGUI/CMakeLists.txt
src/XGUI/XGUI_ContextMenuMgr.cpp
src/XGUI/XGUI_Displayer.cpp
src/XGUI/XGUI_Displayer.h
src/XGUI/XGUI_FacesPanel.cpp
src/XGUI/XGUI_FacesPanel.h
src/XGUI/XGUI_InspectionPanel.cpp
src/XGUI/XGUI_InspectionPanel.h
src/XGUI/XGUI_ModuleConnector.cpp
src/XGUI/XGUI_ModuleConnector.h
src/XGUI/XGUI_OperationMgr.cpp
src/XGUI/XGUI_SelectionMgr.cpp
src/XGUI/XGUI_SelectionMgr.h
src/XGUI/XGUI_Tools.cpp
src/XGUI/XGUI_Workshop.cpp
src/XGUI/XGUI_Workshop.h
test.sh

index d6cf38269b8046626dac346450c1e0ec19024966..5dbe200badf9cb7222a9a2038d16023e3f790d04 100644 (file)
@@ -24,6 +24,8 @@ export PATH=${CPPLINT_ROOT_DIR}:${PATH}
 ##
 
 #------ SHAPER ------
-export PATH=${SHAPER_ROOT_DIR}/bin/salome:${PATH}
-export PYTHONPATH=${SHAPER_ROOT_DIR}/bin/salome:${SHAPER_ROOT_DIR}/lib/python3.6/site-packages/salome:${PYTHONPATH}
+export SHAPER_BIN_DIR=${SHAPER_ROOT_DIR}/bin/salome
+export SHAPER_PYTHON_SCRIPTS_DIR=${SHAPER_ROOT_DIR}/lib/python3.6/site-packages/salome
+export PATH=${SHAPER_BIN_DIR}:${PATH}
+export PYTHONPATH=${SHAPER_BIN_DIR}:${SHAPER_PYTHON_SCRIPTS_DIR}:${PYTHONPATH}
 export LD_LIBRARY_PATH=${SHAPER_ROOT_DIR}/lib/salome:${LD_LIBRARY_PATH}
index a02f0bf2fb81c8b901d93f71b50aa83efe1d1043..ea913123b87290bd257e49b2f009961753ef997d 100644 (file)
@@ -18,7 +18,8 @@
         tooltip="Create named collection of geometry entities"
         icon="icons/Collection/shape_group.png"
         apply_continue="true"
-        helpfile="groupFeature.html">
+        helpfile="groupFeature.html"
+        hidefaces_panel="true">
         <source path="group_widget.xml"/>
       </feature>
 
index 47baeae2ee4da30faf6e77d3bb567fcf84bdd26e..e21246920295d4be1c18071bb24a803ed95f0daf 100644 (file)
@@ -40,6 +40,8 @@ Config_FeatureMessage::Config_FeatureMessage(const Events_ID theId, const void*
   myModal = false;
   myIsTitleInToolbar = true;
   myIsApplyContinue = false;
+  myHideFacesPanel = false;
+  myAbortConfirmation = true;
 }
 
 Config_FeatureMessage::~Config_FeatureMessage()
@@ -238,3 +240,24 @@ void Config_FeatureMessage::setTitleInToolbar(bool theValue)
 {
   myIsTitleInToolbar = theValue;
 }
+
+bool Config_FeatureMessage::isHideFacesPanel() const
+{
+  return myHideFacesPanel;
+}
+
+
+void Config_FeatureMessage::setHideFacesPanel(bool theValue)
+{
+  myHideFacesPanel = theValue;
+}
+
+bool Config_FeatureMessage::isAbortConfirmation() const
+{
+  return myAbortConfirmation;
+}
+
+void Config_FeatureMessage::setAbortConfirmation(bool theValue)
+{
+  myAbortConfirmation = theValue;
+}
index a027bec60b1df6e551dfa516bfba51af2f57ac00..8228256a8b065c06d50c19c2a565030bca14cb7a 100644 (file)
@@ -55,6 +55,8 @@ class Config_FeatureMessage : public Events_Message
   bool myModal;     ///<True if the feature has to be represented by modal dialog box
   bool myIsAutoPreview; ///< Preview computation is performed automatically
   bool myIsTitleInToolbar; ///< False if title should not be displayed in the toolbar
+  bool myHideFacesPanel; ///< Show or Hide HideFaces panel. By default is False
+  bool myAbortConfirmation; ///< Ask confirmation of abort of the feature from user
 
   /// True if the feature can have Apply/Continue button in its property panel
   bool myIsApplyContinue;
@@ -120,6 +122,9 @@ class Config_FeatureMessage : public Events_Message
   CONFIG_EXPORT bool isAutoPreview() const;
   /// If true - title should normally be displayed in the toolbar
   CONFIG_EXPORT bool isTitleInToolbar() const;
+  /// If true - then HideFaces panel has to be shown
+  CONFIG_EXPORT bool isHideFacesPanel() const;
+  CONFIG_EXPORT bool isAbortConfirmation() const;
 
   ///Set feature's Id
   CONFIG_EXPORT void setId(const std::string& id);
@@ -157,6 +162,10 @@ class Config_FeatureMessage : public Events_Message
   CONFIG_EXPORT void setModal(bool isModal);
   ///Set flag to display title in toolbar
   CONFIG_EXPORT void setTitleInToolbar(bool theValue);
+  ///Set flag to display title in toolbar
+  CONFIG_EXPORT void setHideFacesPanel(bool theValue);
+  ///Set flag to display title in toolbar
+  CONFIG_EXPORT void setAbortConfirmation(bool theValue);
   ///Set Apply/Continue state;
   ///If true - the feature can have Apply/Continue button in its property panel
   CONFIG_EXPORT void setApplyContinue(bool isModal);
index 7d93e060e7dc85163cad07be11d650a6b2444239..7254b5577d4b6ef91f7369d3e4d493c001ba3ea8 100644 (file)
@@ -179,10 +179,16 @@ void Config_FeatureReader::fillFeature(xmlNodePtr theFeatureNode,
   if (!aHelpFile.empty())
     outFeatureMessage->setHelpFileName(myLibraryName + "/" + aHelpFile);
 
-  if (isInternal) {
-    //Internal feature has no visual representation.
-    return;
-  }
+  bool isHideFaces = getBooleanAttribute(theFeatureNode, HIDEFACES_PANEL, false);
+  outFeatureMessage->setHideFacesPanel(isHideFaces);
+
+  bool isConfirmAbort = getBooleanAttribute(theFeatureNode, ABORT_CONFIRMATION, true);
+  outFeatureMessage->setAbortConfirmation(isConfirmAbort);
+
+  //if (isInternal) {
+  //  //Internal feature has no visual representation.
+  //  return;
+  //}
 
   std::string aText = Config_Translator::translate(anId, getProperty(theFeatureNode, FEATURE_TEXT));
   outFeatureMessage->setText(aText);
index 32132a196ae9e6ceddf68367e570e4f8d8ca49e8..c0e2fa4b0f920d69c7906d2da8f4cca091005a83 100644 (file)
@@ -35,6 +35,7 @@ const static char* PROPERTY_PANEL_ID = "property_panel_id";
 
 // Widgets
 const static char* WDG_INFO = "label";
+const static char* WDG_UNDOLABEL = "undo_label";
 const static char* WDG_DOUBLEVALUE = "doublevalue";
 const static char* WDG_DOUBLEVALUELABEL = "labelvalue";
 const static char* WDG_INTEGERVALUE = "integervalue";
@@ -77,6 +78,8 @@ const static char* GROUP_TOOLBAR = "toolbar";
 const static char* FEATURE_ICON = "icon";
 const static char* FEATURE_TEXT = "title";
 const static char* HELP_FILE = "helpfile";
+const static char* ABORT_CONFIRMATION = "abort_confirmation";
+const static char* HIDEFACES_PANEL = "hidefaces_panel";
 const static char* FEATURE_KEYSEQUENCE = "keysequence";
 const static char* FEATURE_NESTED = "nested";
 const static char* FEATURE_WHEN_NESTED = "when_nested";
@@ -95,6 +98,7 @@ const static char* ATTR_TOOLTIP = FEATURE_TOOLTIP;
 const static char* ATTR_ICON = FEATURE_ICON;
 const static char* ATTR_LABEL = "label";
 const static char* ATTR_STYLE_SHEET = "styleSheet";
+const static char* ATTR_HTML_STYLE = "isHTML";
 const static char* ATTR_DEFAULT = "default";
 const static char* ATTR_INTERNAL = "internal";
 const static char* ATTR_OBLIGATORY = "obligatory";
index 8f3a826af57d208f111fce64551b01e245b9e8db..055097cb7a2e9f1ea382950e340a0e451e489cee 100644 (file)
@@ -77,7 +77,6 @@ class Config_WidgetAPI
    */
   CONFIG_EXPORT bool getBooleanAttribute(const char* theAttributeName, bool theDefault) const;
 
- protected:
   /// These fields are accessible for ModuleBase_WidgetFactory only
   CONFIG_EXPORT Config_WidgetAPI(std::string theRawXml);
   //! Pass to the next (sibling) node of widget's xml definition. If impossible, returns false
index dcffc622e5cb1e71df9d7bda0b91552fcc9b00b1..62ededcd19ba4e4f2eaf005693787630fe11bf65 100644 (file)
@@ -6,6 +6,7 @@
         title="Point"
         tooltip="Create point"
         icon="icons/Construction/point.png"
+        apply_continue="true"
         helpfile="pointFeature.html">
         <source path="point_widget.xml" />
       </feature>
index fa1ab009e885ea54f52438e9754466c91d7dc332..2d764635a27b543c56fcc5f751cba48a17417922 100644 (file)
@@ -19,6 +19,8 @@
 
 #include "ExchangeAPI_Export.h"
 //--------------------------------------------------------------------------------------
+#include <ExchangePlugin_ExportPart.h>
+//--------------------------------------------------------------------------------------
 #include <ModelAPI_Document.h>
 #include <ModelAPI_Feature.h>
 #include <ModelHighAPI_Tools.h>
@@ -188,4 +190,17 @@ ExportPtr exportToXAO(const std::shared_ptr<ModelAPI_Document> & thePart,
   return ExportPtr(new ExchangeAPI_Export(aFeature, theFilePath, theSelectedShape, "XAO"));
 }
 
+void exportPart(const std::shared_ptr<ModelAPI_Document> & thePart,
+                const std::string & theFilePath,
+                const std::list<ModelHighAPI_Selection> & theSelected)
+{
+  FeaturePtr aFeature = thePart->addFeature(ExchangePlugin_ExportPart::ID());
+  aFeature->string(ExchangePlugin_ExportPart::FILE_PATH_ID())->setValue(theFilePath);
+  if (!theSelected.empty()) {
+    fillAttribute(theSelected,
+        aFeature->selectionList(ExchangePlugin_ExportPart::SELECTION_LIST_ID()));
+  }
+  // restart transaction to execute and delete the macro-feature
+  apply();
+}
 //--------------------------------------------------------------------------------------
index 6cfddfd8f65d13500e2b984bb59d85dc39a4dff5..c976d43a31ecbed9b9c10ea39de875ea72e1e04f 100644 (file)
@@ -123,6 +123,15 @@ ExportPtr exportToXAO(const std::shared_ptr<ModelAPI_Document> & thePart,
   const std::string & theAuthor = std::string(),
   const std::string & theGeometryName = std::string());
 
+
+/** \ingroup CPPHighAPI
+ *  \brief Export selected features or the whole part to the binary file.
+ */
+EXCHANGEAPI_EXPORT void exportPart(
+    const std::shared_ptr<ModelAPI_Document> & thePart,
+    const std::string & theFilePath,
+    const std::list<ModelHighAPI_Selection> & theSelected = std::list<ModelHighAPI_Selection>());
+
 //--------------------------------------------------------------------------------------
 //--------------------------------------------------------------------------------------
 #endif /* SRC_EXCHANGEAPI_EXCHANGEAPI_EXPORT_H_ */
index d360c95a76ca54f2bc23866b2f33c8db82a49d2e..e585f971e036d7b379c9f96b1eaf44f916556329 100644 (file)
 
 #include "ExchangeAPI_Import.h"
 //--------------------------------------------------------------------------------------
+#include <ExchangePlugin_ImportPart.h>
+//--------------------------------------------------------------------------------------
 #include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Services.h>
 #include <ModelHighAPI_Tools.h>
 //--------------------------------------------------------------------------------------
 #include <algorithm>
@@ -94,3 +97,24 @@ ImportPtr addImport(
   std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(ExchangeAPI_Import::ID());
   return ImportPtr(new ExchangeAPI_Import(aFeature, theFilePath));
 }
+
+void importPart(const std::shared_ptr<ModelAPI_Document> & thePart,
+                const std::string & theFilePath,
+                const ModelHighAPI_Reference & theAfterThis)
+{
+  static const bool THE_VISIBLE_FEATURE = false;
+  FeaturePtr aCurrentFeature;
+  if (theAfterThis.feature()) {
+    aCurrentFeature = thePart->currentFeature(THE_VISIBLE_FEATURE);
+    thePart->setCurrentFeature(theAfterThis.feature(), THE_VISIBLE_FEATURE);
+  }
+
+  FeaturePtr aFeature = thePart->addFeature(ExchangePlugin_ImportPart::ID());
+  aFeature->string(ExchangePlugin_ImportPart::FILE_PATH_ID())->setValue(theFilePath);
+  // restart transaction to execute and delete the macro-feature
+  apply();
+
+  // restore current feature
+  if (aCurrentFeature)
+    thePart->setCurrentFeature(aCurrentFeature, THE_VISIBLE_FEATURE);
+}
index 1713c9271bfc4eba6b5ae063e06e4cd45829c825..57793c725c106343ab44a5ac76a2877248be9b93 100644 (file)
@@ -29,6 +29,8 @@
 
 #include <ModelHighAPI_Interface.h>
 #include <ModelHighAPI_Macro.h>
+#include <ModelHighAPI_Reference.h>
+#include <ModelHighAPI_Selection.h>
 //--------------------------------------------------------------------------------------
 /**\class ExchangeAPI_Import
  * \ingroup CPPHighAPI
@@ -72,6 +74,14 @@ EXCHANGEAPI_EXPORT
 ImportPtr addImport(const std::shared_ptr<ModelAPI_Document> & thePart,
                     const std::string & theFilePath);
 
+/** \ingroup CPPHighAPI
+ *  \brief Import features from the file to the document after the current feature (or to the end).
+ */
+EXCHANGEAPI_EXPORT void importPart(
+    const std::shared_ptr<ModelAPI_Document> & thePart,
+    const std::string & theFilePath,
+    const ModelHighAPI_Reference & theAfterThis = ModelHighAPI_Reference());
+
 //--------------------------------------------------------------------------------------
 //--------------------------------------------------------------------------------------
 #endif /* SRC_EXCHANGEAPI_EXCHANGEAPI_IMPORT_H_ */
index 2a9c196f4905e9b6f8c94b90d220c942ea0d66c9..d580a700f8bf7a5a652fb5e342adad432ba81a11 100644 (file)
@@ -27,6 +27,8 @@ INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/Events
                     ${PROJECT_SOURCE_DIR}/src/GeomAPI
                     ${PROJECT_SOURCE_DIR}/src/GeomAlgoAPI
                     ${PROJECT_SOURCE_DIR}/src/XAO
+                    ${PROJECT_SOURCE_DIR}/src/ConstructionPlugin
+                    ${PROJECT_SOURCE_DIR}/src/PartSetPlugin
 )
 
 SET(PROJECT_HEADERS
@@ -37,6 +39,8 @@ SET(PROJECT_HEADERS
     ExchangePlugin_Validators.h
     ExchangePlugin_Tools.h
     ExchangePlugin_Dump.h
+    ExchangePlugin_ImportPart.h
+    ExchangePlugin_ExportPart.h
 )
 
 SET(PROJECT_SOURCES
@@ -46,6 +50,8 @@ SET(PROJECT_SOURCES
     ExchangePlugin_Validators.cpp
     ExchangePlugin_Tools.cpp
     ExchangePlugin_Dump.cpp
+    ExchangePlugin_ImportPart.cpp
+    ExchangePlugin_ExportPart.cpp
 )
 
 SET(XML_RESOURCES
@@ -79,11 +85,42 @@ INSTALL(FILES ${XML_RESOURCES} DESTINATION ${SHAPER_INSTALL_XML_RESOURCES})
 INSTALL(DIRECTORY icons/ DESTINATION ${SHAPER_INSTALL_XML_RESOURCES}/icons/Exchange)
 INSTALL(FILES ${TEXT_RESOURCES} DESTINATION ${SHAPER_INSTALL_XML_RESOURCES})
 
-ADD_UNIT_TESTS(TestImport.py
-               TestExport.py
-               Test2290.py
-               Test2459.py
-               TestExportToXAOWithFields.py
-               TestExportToXAOWithGroupNotUpdated.py
-               TestExport_FiniteValidator.py
+ADD_UNIT_TESTS(
+  TestImport.py
+  TestExport.py
+  Test2290.py
+  Test2459.py
+  TestExportToXAOWithFields.py
+  TestExportToXAOWithGroupNotUpdated.py
+  TestExport_FiniteValidator.py
+  TestExportPart_Failure_1.py
+  TestExportPart_Failure_2.py
+  TestExportPart_Failure_3.py
+  TestExportPart_FullPartSet.py
+  TestExportPart_FullPart_1.py
+  TestExportPart_FullPart_2.py
+  TestExportPart_PartSet.py
+  TestExportPart_Results_1.py
+  TestExportPart_Results_2.py
+  TestExportPart_Results_3.py
+  TestExportPart_Results_4.py
+  TestExportPart_Results_5.py
+  TestExportPart_Results_6.py
+  TestExportPart_Results_7.py
+  TestExportPart_Results_8.py
+  TestImportPart_AfterCurrent_1.py
+  TestImportPart_AfterCurrent_2.py
+  TestImportPart_AfterLast_1.py
+  TestImportPart_AfterLast_2.py
+  TestImportPart_AfterLast_3.py
+  TestImportPart_AfterLast_4.py
+  TestImportPart_AfterLast_5.py
+  TestImportPart_AfterLast_6.py
+  TestImportPart_Construction_1.py
+  TestImportPart_Construction_2.py
+  TestImportPart_Construction_3.py
+  TestImportPart_Construction_4.py
+  TestImportPart_Multiple.py
+  TestImportPart_ToEmptyPart.py
+  TestImportPart_ToEmptyPartSet.py
 )
diff --git a/src/ExchangePlugin/ExchangePlugin_ExportPart.cpp b/src/ExchangePlugin/ExchangePlugin_ExportPart.cpp
new file mode 100644 (file)
index 0000000..2957ce7
--- /dev/null
@@ -0,0 +1,278 @@
+// 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
+//
+
+#include <ExchangePlugin_ExportPart.h>
+
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_AttributeString.h>
+#include <ModelAPI_ResultConstruction.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Validator.h>
+
+#include <ConstructionPlugin_Axis.h>
+#include <ConstructionPlugin_Plane.h>
+#include <ConstructionPlugin_Point.h>
+
+#include <Events_InfoMessage.h>
+
+#include <PartSetPlugin_Part.h>
+
+#include <sstream>
+
+// Obtain all features to be exported to get the list of selected results.
+static void collectFeatures(DocumentPtr theDocument,
+                            AttributeSelectionListPtr theSelected,
+                            std::list<FeaturePtr>& theExport);
+// Obtain all constuction elements of the document.
+static void collectConstructions(DocumentPtr theDocument, std::list<FeaturePtr>& theExport);
+// Check features could be exported. The following features cannot be exported:
+// * non-construction result (Part) when exporting the PartSet;
+// * features, which refer to objects from another document.
+// Returns true if all features can be exported.
+static bool verifyExport(const std::list<FeaturePtr>& theFeatures,
+                         std::list<FeaturePtr>& theExternalReferences,
+                         std::list<FeaturePtr>& theExportedParts);
+
+
+ExchangePlugin_ExportPart::ExchangePlugin_ExportPart()
+{
+}
+
+void ExchangePlugin_ExportPart::initAttributes()
+{
+  data()->addAttribute(FILE_PATH_ID(), ModelAPI_AttributeString::typeId());
+  data()->addAttribute(FILE_FORMAT_ID(), ModelAPI_AttributeString::typeId());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), FILE_FORMAT_ID());
+  data()->addAttribute(SELECTION_LIST_ID(), ModelAPI_AttributeSelectionList::typeId());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), SELECTION_LIST_ID());
+}
+
+void ExchangePlugin_ExportPart::execute()
+{
+  AttributeStringPtr aFilePathAttr = string(FILE_PATH_ID());
+  std::string aFilename = aFilePathAttr->value();
+  if (aFilename.empty()) {
+    setError("File name is empty.");
+    return;
+  }
+
+  std::list<FeaturePtr> aFeaturesToExport;
+
+  DocumentPtr anExportDoc = document();
+  DocumentPtr aPartSetDoc = ModelAPI_Session::get()->moduleDocument();
+  AttributeSelectionListPtr aSelected = selectionList(SELECTION_LIST_ID());
+  if (aSelected && aSelected->size() == 0 && anExportDoc == aPartSetDoc) {
+    // no result is selected, thus have to export all features of the current document,
+    // but the document is a PartSet; and it is forbidden to copy results of Parts,
+    // thus copy construction elements only
+    collectConstructions(anExportDoc, aFeaturesToExport);
+  }
+  else
+    collectFeatures(anExportDoc, aSelected, aFeaturesToExport);
+
+  if (aFeaturesToExport.empty()) {
+    Events_InfoMessage(getKind(), "Selected features cannot be exported from the document.").send();
+    return;
+  }
+
+  // remove 'ExportPart' feature if any
+  if (aFeaturesToExport.back()->getKind() == ExchangePlugin_ExportPart::ID())
+    aFeaturesToExport.pop_back();
+
+  std::list<FeaturePtr> anExternalLinks, aReferredParts;
+  if (!verifyExport(aFeaturesToExport, anExternalLinks, aReferredParts)) {
+    if (!anExternalLinks.empty()) {
+      // collect names of features as a string
+      std::ostringstream aListOfFeatures;
+      for (std::list<FeaturePtr>::iterator anIt = anExternalLinks.begin();
+           anIt != anExternalLinks.end(); ++anIt) {
+        if (anIt != anExternalLinks.begin())
+          aListOfFeatures << ", ";
+        aListOfFeatures << "'" << (*anIt)->name() << "'";
+      }
+
+      std::string aMessage = "The selected results were created using external references "
+                             "outside of this Part from features %1. "
+                             "Please, remove these references or select another "
+                             "sub-set of results to be able to export.";
+      Events_InfoMessage(getKind(), aMessage).arg(aListOfFeatures.str()).send();
+    }
+    if (!aReferredParts.empty()) {
+      // collect names of parts as a string
+      std::ostringstream aListOfParts;
+      for (std::list<FeaturePtr>::iterator anIt = aReferredParts.begin();
+           anIt != aReferredParts.end(); ++anIt) {
+        if (anIt != aReferredParts.begin())
+          aListOfParts << ", ";
+        aListOfParts << "'" << (*anIt)->name() << "'";
+      }
+
+      std::string aMessage = "The selected results were created using references "
+                             "to results of Parts %1. Please, remove these references "
+                             "or select another sub-set of results to be able to export.";
+      Events_InfoMessage(getKind(), aMessage).arg(aListOfParts.str()).send();
+    }
+    // should not export anything
+    aFeaturesToExport.clear();
+  }
+
+  if (!aFeaturesToExport.empty()) {
+    // save the document
+    if (!anExportDoc->save(aFilename.c_str(), aFeaturesToExport))
+      setError("Cannot save the document.");
+  }
+}
+
+
+// ================================     Auxiliary functions     ===================================
+
+static bool isCoordinate(FeaturePtr theFeature)
+{
+  return !theFeature->isInHistory() &&
+          (theFeature->getKind() == ConstructionPlugin_Point::ID() ||
+           theFeature->getKind() == ConstructionPlugin_Axis::ID() ||
+           theFeature->getKind() == ConstructionPlugin_Plane::ID());
+}
+
+static void allReferencedFeatures(const std::set<FeaturePtr>& theFeatures,
+                                  std::set<FeaturePtr>& theReferencedFeatures)
+{
+  std::set<FeaturePtr> aReferences;
+  for (std::set<FeaturePtr>::const_iterator anIt = theFeatures.begin();
+       anIt != theFeatures.end(); ++anIt) {
+    theReferencedFeatures.insert(*anIt);
+
+    std::list<std::pair<std::string, std::list<ObjectPtr> > > aRefs;
+    (*anIt)->data()->referencesToObjects(aRefs);
+
+    for (std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator aRIt = aRefs.begin();
+         aRIt != aRefs.end(); ++aRIt) {
+      for (std::list<ObjectPtr>::iterator anObjIt = aRIt->second.begin();
+           anObjIt != aRIt->second.end(); ++anObjIt) {
+        FeaturePtr aFeature = ModelAPI_Feature::feature(*anObjIt);
+        if (aFeature && !isCoordinate(aFeature) &&
+            theReferencedFeatures.find(aFeature) == theReferencedFeatures.end())
+          aReferences.insert(aFeature);
+      }
+    }
+  }
+
+  if (!aReferences.empty())
+    allReferencedFeatures(aReferences, theReferencedFeatures);
+}
+
+void collectFeatures(DocumentPtr theDocument,
+                     AttributeSelectionListPtr theSelected,
+                     std::list<FeaturePtr>& theExport)
+{
+  theExport = theDocument->allFeatures();
+
+  // remove all features after the current one
+  FeaturePtr aCurrentFeature = theDocument->currentFeature(false);
+  std::list<FeaturePtr>::iterator anIt = theExport.begin();
+  for (; anIt != theExport.end(); ++anIt)
+    if (*anIt == aCurrentFeature) {
+      theExport.erase(++anIt, theExport.end());
+      break;
+    }
+
+  if (!theSelected || theSelected->size() == 0) {
+    // nothing is selected, return all features of the document
+    return;
+  }
+
+  // collect initial list of features basing on the selected results
+  std::set<FeaturePtr> aFeaturesToExport;
+  for (int anIndex = 0, aSize = theSelected->size(); anIndex < aSize; ++anIndex) {
+    AttributeSelectionPtr aCurrent = theSelected->value(anIndex);
+    FeaturePtr aCurrentFeature = ModelAPI_Feature::feature(aCurrent->context());
+    if (aCurrentFeature)
+      aFeaturesToExport.insert(aCurrentFeature);
+  }
+  // recursively collect all features used for the selected results
+  allReferencedFeatures(aFeaturesToExport, aFeaturesToExport);
+
+  // remove the features which are not affect the selected results
+  anIt = theExport.begin();
+  while (anIt != theExport.end()) {
+    if (aFeaturesToExport.find(*anIt) == aFeaturesToExport.end()) {
+      std::list<FeaturePtr>::iterator aRemoveIt = anIt++;
+      theExport.erase(aRemoveIt);
+    }
+    else
+      ++anIt;
+  }
+}
+
+void collectConstructions(DocumentPtr theDocument, std::list<FeaturePtr>& theExport)
+{
+  theExport = theDocument->allFeatures();
+  // keep constructions only
+  std::list<FeaturePtr>::iterator anIt = theExport.begin();
+  while (anIt != theExport.end()) {
+    FeaturePtr aCurFeature = *anIt;
+    ResultPtr aCurResult = aCurFeature->lastResult();
+
+    bool isApplicable =
+        (!aCurResult || aCurResult->groupName() == ModelAPI_ResultConstruction::group()) &&
+        !isCoordinate(aCurFeature);
+
+    if (isApplicable)
+      ++anIt;
+    else {
+      std::list<FeaturePtr>::iterator aRemoveIt = anIt++;
+      theExport.erase(aRemoveIt);
+    }
+  }
+}
+
+bool verifyExport(const std::list<FeaturePtr>& theFeatures,
+                  std::list<FeaturePtr>& theExternalReferences,
+                  std::list<FeaturePtr>& theExportedParts)
+{
+  for (std::list<FeaturePtr>::const_iterator anIt = theFeatures.begin();
+       anIt != theFeatures.end(); ++anIt) {
+    // full part should not be exported
+    if ((*anIt)->getKind() == PartSetPlugin_Part::ID())
+      theExportedParts.push_back(*anIt);
+
+    DocumentPtr aDoc = (*anIt)->document();
+
+    std::list<std::pair<std::string, std::list<ObjectPtr> > > aRefs;
+    (*anIt)->data()->referencesToObjects(aRefs);
+    std::list<std::pair<std::string, std::list<ObjectPtr> > >::iterator aRIt = aRefs.begin();
+    for (;  aRIt != aRefs.end(); ++aRIt) {
+      for (std::list<ObjectPtr>::iterator anObjIt = aRIt->second.begin();
+           anObjIt != aRIt->second.end(); ++anObjIt) {
+        FeaturePtr aFeature = ModelAPI_Feature::feature(*anObjIt);
+        if (aFeature) {
+          // feature refers to external entity,
+          // which is neither the Origin nor coordinate axis or plane
+          if (aFeature->document() != aDoc && !isCoordinate(aFeature))
+            theExternalReferences.push_back(*anIt);
+          // feature refers to result of a part
+          if (aFeature->getKind() == PartSetPlugin_Part::ID())
+            theExportedParts.push_back(*anIt);
+        }
+      }
+    }
+  }
+
+  return theExternalReferences.empty() && theExportedParts.empty();
+}
diff --git a/src/ExchangePlugin/ExchangePlugin_ExportPart.h b/src/ExchangePlugin/ExchangePlugin_ExportPart.h
new file mode 100644 (file)
index 0000000..504d777
--- /dev/null
@@ -0,0 +1,84 @@
+// 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
+//
+
+#ifndef EXCHANGEPLUGIN_EXPORTPART_H_
+#define EXCHANGEPLUGIN_EXPORTPART_H_
+
+#include <ExchangePlugin.h>
+#include <ModelAPI_Feature.h>
+
+/**
+ * \class ExchangePlugin_ExportPart
+ * \ingroup Plugins
+ * \brief Feature for export some results of a Part to the binary format for the further import.
+ */
+class ExchangePlugin_ExportPart : public ModelAPI_Feature
+{
+public:
+  /// Feature kind
+  inline static const std::string& ID()
+  {
+    static const std::string MY_EXPORT_ID("ExportPart");
+    return MY_EXPORT_ID;
+  }
+  /// attribute name of file path
+  inline static const std::string& FILE_PATH_ID()
+  {
+    static const std::string MY_FILE_PATH_ID("file_path");
+    return MY_FILE_PATH_ID;
+  }
+  /// attribute name of file format
+  inline static const std::string& FILE_FORMAT_ID()
+  {
+    static const std::string MY_FILE_FORMAT_ID("file_format");
+    return MY_FILE_FORMAT_ID;
+  }
+  /// attribute name of selection list
+  inline static const std::string& SELECTION_LIST_ID()
+  {
+    static const std::string MY_SELECTION_LIST_ID("selection_list");
+    return MY_SELECTION_LIST_ID;
+  }
+  /// Default constructor
+  ExchangePlugin_ExportPart();
+
+  /// Returns the unique kind of a feature
+  EXCHANGEPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    return ExchangePlugin_ExportPart::ID();
+  }
+
+  /// Request for initialization of data model of the feature: adding all attributes
+  EXCHANGEPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Computes or recomputes the results
+  EXCHANGEPLUGIN_EXPORT virtual void execute();
+
+  /// Returns true if this feature is used as macro: creates other features and then removed.
+  EXCHANGEPLUGIN_EXPORT virtual bool isMacro() const { return true; }
+
+  /// Reimplemented from ModelAPI_Feature::isPreviewNeeded(). Returns false.
+  EXCHANGEPLUGIN_EXPORT virtual bool isPreviewNeeded() const { return false; }
+
+  /// Do not put in history.
+  /// Since it is not a macro, it is not deleted, but we don't want to see it.
+  bool isInHistory()  { return false; }
+};
+
+#endif /* EXCHANGEPLUGIN_EXPORTPART_H_ */
diff --git a/src/ExchangePlugin/ExchangePlugin_ImportPart.cpp b/src/ExchangePlugin/ExchangePlugin_ImportPart.cpp
new file mode 100644 (file)
index 0000000..14f2c19
--- /dev/null
@@ -0,0 +1,193 @@
+// 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
+//
+
+#include <ExchangePlugin_ImportPart.h>
+
+#include <ModelAPI_AttributeString.h>
+#include <ModelAPI_ResultPart.h>
+#include <ModelAPI_Session.h>
+
+#include <PartSetPlugin_Part.h>
+
+#include <map>
+#include <sstream>
+
+// Update names of imported features/results concurent with existing objects.
+static void correntNonUniqueNames(DocumentPtr theDocument, std::list<FeaturePtr>& theImported);
+
+ExchangePlugin_ImportPart::ExchangePlugin_ImportPart()
+{
+}
+
+void ExchangePlugin_ImportPart::initAttributes()
+{
+  data()->addAttribute(FILE_PATH_ID(), ModelAPI_AttributeString::typeId());
+}
+
+void ExchangePlugin_ImportPart::execute()
+{
+  AttributeStringPtr aFilePathAttr = string(FILE_PATH_ID());
+  std::string aFilename = aFilePathAttr->value();
+  if (aFilename.empty()) {
+    setError("File name is empty.");
+    return;
+  }
+
+  // load the file into the active document
+  SessionPtr aSession = ModelAPI_Session::get();
+  DocumentPtr aDoc = document();
+  bool isPartSet = aDoc == aSession->moduleDocument();
+  std::list<FeaturePtr> anImportedFeatures;
+  bool isOk = aDoc->import(aFilename.c_str(), anImportedFeatures, isPartSet);
+  if (!isOk && isPartSet) {
+    // there are features not appropriate for PartSet,
+    // create new part and load there
+    FeaturePtr aPartFeature = aDoc->addFeature(PartSetPlugin_Part::ID());
+    ResultPartPtr aPartResult;
+    if (aPartFeature) {
+      aPartFeature->execute();
+      aPartResult = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aPartFeature->lastResult());
+    }
+    if (aPartResult) {
+      aDoc = aPartResult->partDoc();
+      isOk = aDoc->import(aFilename.c_str(), anImportedFeatures);
+    }
+  }
+  if (isOk)
+    correntNonUniqueNames(aDoc, anImportedFeatures);
+  else
+    setError("Cannot import the document.");
+}
+
+
+// ================================     Auxiliary functions     ===================================
+
+typedef std::map<std::string, std::map<std::string, std::set<int> > > ObjectNameMap;
+
+bool splitName(std::string& theName, int& theIndex)
+{
+  size_t aLastUndercore = theName.find_last_of('_');
+  bool isOk = aLastUndercore != std::string::npos;
+  if (isOk) {
+    char* isNumber;
+    std::string anIndexStr = theName.substr(aLastUndercore + 1);
+    theIndex = std::strtol(anIndexStr.c_str(), &isNumber, 10);
+    isOk = isNumber != 0;
+    if (isOk)
+      theName.erase(aLastUndercore);
+  }
+  return isOk;
+}
+
+void addIndexedName(const ObjectPtr& theObject, ObjectNameMap& theIndexedNames)
+{
+  std::string aName = theObject->data()->name();
+  std::string aGroup = theObject->groupName();
+  int anIndex = 0;
+  bool isIndexed = splitName(aName, anIndex);
+  std::set<int>& anIndices = theIndexedNames[aGroup][aName];
+  if (isIndexed)
+    anIndices.insert(anIndex);
+}
+
+// Collect names of features and results in the document before the import.
+// The name of indexed feature/result will be split to the name and the index. For example ,
+// 'Point_1', 'Point_2' will be placed at the same key with the set of corrsponding indices:
+// 'Point_1', 'Point_2' => {'Point', [1, 2]}.
+// Thus, the new point should have index 3 and therefore the name 'Point_3'.
+static void collectOldNames(DocumentPtr theDocument, std::list<FeaturePtr>& theAvoided,
+                            ObjectNameMap& theIndexedNames)
+{
+  std::list<FeaturePtr> anAllFeatures = theDocument->allFeatures();
+  std::list<FeaturePtr>::iterator aFIt = anAllFeatures.begin();
+  std::list<FeaturePtr>::iterator anAvoidIt = theAvoided.begin();
+  for (; aFIt != anAllFeatures.end(); ++aFIt) {
+    if (anAvoidIt != theAvoided.end() && *aFIt == *anAvoidIt) {
+      // skip this feature
+      ++anAvoidIt;
+      continue;
+    }
+
+    // store name of feature
+    addIndexedName(*aFIt, theIndexedNames);
+    // store names of results
+    const std::list<ResultPtr>& aResults = (*aFIt)->results();
+    for (std::list<ResultPtr>::const_iterator aRIt = aResults.begin();
+         aRIt != aResults.end(); ++aRIt)
+      addIndexedName(*aRIt, theIndexedNames);
+  }
+}
+
+static std::string uniqueName(const ObjectPtr& theObject, ObjectNameMap& theExistingNames)
+{
+  std::string aName = theObject->data()->name();
+  std::string aGroup = theObject->groupName();
+  int anIndex = 1;
+  splitName(aName, anIndex);
+
+  ObjectNameMap::iterator aFoundGroup = theExistingNames.find(aGroup);
+  bool isUnique = aFoundGroup == theExistingNames.end();
+
+  std::map<std::string, std::set<int> >::iterator aFound;
+  if (!isUnique) {
+    aFound = aFoundGroup->second.find(aName);
+    isUnique = aFound == aFoundGroup->second.end();
+  }
+
+  if (isUnique) {
+    // name is unique
+    aName = theObject->data()->name();
+    addIndexedName(theObject, theExistingNames);
+  }
+  else {
+    // search the appropriate index
+    std::set<int>::iterator aFoundIndex = aFound->second.find(anIndex);
+    for (; aFoundIndex != aFound->second.end(); ++aFoundIndex, ++anIndex)
+      if (anIndex != *aFoundIndex)
+        break;
+    // compose the new name
+    std::ostringstream aNewName;
+    aNewName << aName << "_" << anIndex;
+    aName = aNewName.str();
+    // add new index
+    aFound->second.insert(anIndex);
+  }
+
+  return aName;
+}
+
+void correntNonUniqueNames(DocumentPtr theDocument, std::list<FeaturePtr>& theImported)
+{
+  ObjectNameMap aNames;
+  collectOldNames(theDocument, theImported, aNames);
+
+  for (std::list<FeaturePtr>::iterator anIt = theImported.begin();
+       anIt != theImported.end(); ++anIt) {
+    // update name of feature
+    std::string aNewName = uniqueName(*anIt, aNames);
+    (*anIt)->data()->setName(aNewName);
+    // update names of results
+    const std::list<ResultPtr>& aResults = (*anIt)->results();
+    for (std::list<ResultPtr>::const_iterator aRIt = aResults.begin();
+         aRIt != aResults.end(); ++aRIt) {
+      aNewName = uniqueName(*aRIt, aNames);
+      (*aRIt)->data()->setName(aNewName);
+    }
+  }
+}
diff --git a/src/ExchangePlugin/ExchangePlugin_ImportPart.h b/src/ExchangePlugin/ExchangePlugin_ImportPart.h
new file mode 100644 (file)
index 0000000..b10ba54
--- /dev/null
@@ -0,0 +1,68 @@
+// 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
+//
+
+#ifndef EXCHANGEPLUGIN_IMPORTPART_H_
+#define EXCHANGEPLUGIN_IMPORTPART_H_
+
+#include <ExchangePlugin.h>
+#include <ModelAPI_Feature.h>
+
+/**
+ * \class ExchangePlugin_ImportPart
+ * \ingroup Plugins
+ * \brief Feature for import the structure of Part into the current document.
+ */
+class ExchangePlugin_ImportPart : public ModelAPI_Feature
+{
+public:
+  /// Feature kind
+  inline static const std::string& ID()
+  {
+    static const std::string MY_IMPORT_ID("ImportPart");
+    return MY_IMPORT_ID;
+  }
+  /// attribute name of file path
+  inline static const std::string& FILE_PATH_ID()
+  {
+    static const std::string MY_FILE_PATH_ID("file_path");
+    return MY_FILE_PATH_ID;
+  }
+  /// Default constructor
+  ExchangePlugin_ImportPart();
+
+  /// Returns the unique kind of a feature
+  EXCHANGEPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    return ExchangePlugin_ImportPart::ID();
+  }
+
+  /// Request for initialization of data model of the feature: adding all attributes
+  EXCHANGEPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Computes or recomputes the results
+  EXCHANGEPLUGIN_EXPORT virtual void execute();
+
+  /// Returns true if this feature is used as macro: creates other features and then removed.
+  EXCHANGEPLUGIN_EXPORT virtual bool isMacro() const { return true; }
+
+  /// Reimplemented from ModelAPI_Feature::isPreviewNeeded(). Returns false.
+  EXCHANGEPLUGIN_EXPORT virtual bool isPreviewNeeded() const { return false; }
+};
+
+#endif /* EXCHANGEPLUGIN_IMPORTPART_H_ */
index 34c3c1b61a3f923ba84384e37867f86c1e235717..3d07a73cd645f9c3bf14b0a61dc3e9b8ed3f4e3f 100644 (file)
@@ -21,6 +21,8 @@
 #include <ExchangePlugin_Dump.h>
 #include <ExchangePlugin_ImportFeature.h>
 #include <ExchangePlugin_ExportFeature.h>
+#include <ExchangePlugin_ImportPart.h>
+#include <ExchangePlugin_ExportPart.h>
 #include <ExchangePlugin_Validators.h>
 
 #include <Config_PropManager.h>
@@ -53,6 +55,12 @@ FeaturePtr ExchangePlugin_Plugin::createFeature(std::string theFeatureID)
   if (theFeatureID == ExchangePlugin_ExportFeature::ID()) {
     return FeaturePtr(new ExchangePlugin_ExportFeature);
   } else
+  if (theFeatureID == ExchangePlugin_ImportPart::ID()) {
+    return FeaturePtr(new ExchangePlugin_ImportPart);
+  } else
+  if (theFeatureID == ExchangePlugin_ExportPart::ID()) {
+    return FeaturePtr(new ExchangePlugin_ExportPart);
+  } else
   if (theFeatureID == ExchangePlugin_Dump::ID()) {
     return FeaturePtr(new ExchangePlugin_Dump);
   }
index 774af05d1efa1fd054a79aa5699b995bb3c6d5af..992e2357b8bce6ab31b7e56b96adf3e75b1578a2 100644 (file)
       <translation>Attribute %1 is not a string.</translation>
     </message>
   </context>
+
+  <context>
+    <name>Export:ExchangePlugin_ExportPart</name>
+    <message>
+      <source>Cannot save the document.</source>
+      <translation>Cannot save the document.</translation>
+    </message>
+    <message>
+      <source>Selected features cannot be exported from the document.</source>
+      <translation>Selected features cannot be exported from the document.</translation>
+    </message>
+    <message>
+      <source>The selected results were created using external references outside of this Part from features %1. Please, remove these references or select another sub-set of results to be able to export.</source>
+      <translation>The selected results were created using external references outside of this Part from features %1. Please, remove these references or select another sub-set of results to be able to export.</translation>
+    </message>
+    <message>
+      <source>The selected results were created using references to results of Parts %1. Please, remove these references or select another sub-set of results to be able to export.</source>
+      <translation>The selected results were created using references to results of Parts %1. Please, remove these references or select another sub-set of results to be able to export.</translation>
+    </message>
+  </context>
+
+  <context>
+    <name>Import:ExchangePlugin_ImportPart</name>
+    <message>
+      <source>Cannot import the document.</source>
+      <translation>Cannot import the document.</translation>
+    </message>
+  </context>
 </TS>
diff --git a/src/ExchangePlugin/Test/TestExportPart_Failure_1.py b/src/ExchangePlugin/Test/TestExportPart_Failure_1.py
new file mode 100644 (file)
index 0000000..65ce866
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+model.begin()
+model.exportPart(partSet, filename, [Part_1.result()])
+model.end()
+assert not os.path.exists(filename), "ERROR: Exporting result of Part_1 should fail"
diff --git a/src/ExchangePlugin/Test/TestExportPart_Failure_2.py b/src/ExchangePlugin/Test/TestExportPart_Failure_2.py
new file mode 100644 (file)
index 0000000..ceca5f1
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Box_1
+
+model.begin()
+model.exportPart(Part_1_doc, filename, [featureToExport.result()])
+model.end()
+assert not os.path.exists(filename), "ERROR: Exporting of {} from document {} should fail".format(featureToExport.name(), Part_1.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Failure_3.py b/src/ExchangePlugin/Test/TestExportPart_Failure_3.py
new file mode 100644 (file)
index 0000000..8f3e467
--- /dev/null
@@ -0,0 +1,40 @@
+# Copyright (C) 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
+
+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)
+model.do()
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Part_1/[Box_1_1/Front][Box_1_1/Right][Box_1_1/Top]"))
+Translation_1 = model.addTranslation(partSet, [model.selection("COMPOUND", "Part_1/")], model.selection("EDGE", "Axis_4"), 10)
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+model.begin()
+model.exportPart(partSet, filename)
+model.end()
+assert not os.path.exists(filename), "ERROR: Exporting of PartSet should fail"
diff --git a/src/ExchangePlugin/Test/TestExportPart_FullPartSet.py b/src/ExchangePlugin/Test/TestExportPart_FullPartSet.py
new file mode 100644 (file)
index 0000000..5e5f8ac
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+model.begin()
+model.exportPart(partSet, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Failed to export full PartSet"
diff --git a/src/ExchangePlugin/Test/TestExportPart_FullPart_1.py b/src/ExchangePlugin/Test/TestExportPart_FullPart_1.py
new file mode 100644 (file)
index 0000000..e0eeee3
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert not os.path.exists(filename), "ERROR: Exporting of Part_1 should fail"
diff --git a/src/ExchangePlugin/Test/TestExportPart_FullPart_2.py b/src/ExchangePlugin/Test/TestExportPart_FullPart_2.py
new file mode 100644 (file)
index 0000000..6b741ab
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+model.begin()
+model.exportPart(Part_2_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export Part_2 to file {}".format(filename)
diff --git a/src/ExchangePlugin/Test/TestExportPart_PartSet.py b/src/ExchangePlugin/Test/TestExportPart_PartSet.py
new file mode 100644 (file)
index 0000000..3c8b142
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+model.begin()
+model.exportPart(partSet, filename, [Axis_4.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export construction elements of PartSet"
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_1.py b/src/ExchangePlugin/Test/TestExportPart_Results_1.py
new file mode 100644 (file)
index 0000000..6c4266e
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Point_2
+
+model.begin()
+model.exportPart(partSet, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_2.py b/src/ExchangePlugin/Test/TestExportPart_Results_2.py
new file mode 100644 (file)
index 0000000..f2c9ac6
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Axis_4
+
+model.begin()
+model.exportPart(partSet, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_3.py b/src/ExchangePlugin/Test/TestExportPart_Results_3.py
new file mode 100644 (file)
index 0000000..0b723a2
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Sketch_1
+
+model.begin()
+model.exportPart(Part_1_doc, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_4.py b/src/ExchangePlugin/Test/TestExportPart_Results_4.py
new file mode 100644 (file)
index 0000000..a02183e
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Extrusion_1
+
+model.begin()
+model.exportPart(Part_1_doc, filename, [featureToExport.result()])
+model.end()
+assert not os.path.exists(filename), "ERROR: Exporting of {} should fail".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_5.py b/src/ExchangePlugin/Test/TestExportPart_Results_5.py
new file mode 100644 (file)
index 0000000..b3753ed
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Sketch_2
+
+model.begin()
+model.exportPart(Part_1_doc, filename, [featureToExport.result()])
+model.end()
+assert not os.path.exists(filename), "ERROR: Exporting of {} should fail".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_6.py b/src/ExchangePlugin/Test/TestExportPart_Results_6.py
new file mode 100644 (file)
index 0000000..0afd739
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Revolution_1
+
+model.begin()
+model.exportPart(Part_1_doc, filename, [featureToExport.result()])
+model.end()
+assert not os.path.exists(filename), "ERROR: Exporting of {} should fail".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_7.py b/src/ExchangePlugin/Test/TestExportPart_Results_7.py
new file mode 100644 (file)
index 0000000..6dc1b76
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Box_1
+
+model.begin()
+model.exportPart(Part_2_doc, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestExportPart_Results_8.py b/src/ExchangePlugin/Test/TestExportPart_Results_8.py
new file mode 100644 (file)
index 0000000..1a03585
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 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 SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+import os
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Translation_1
+
+model.begin()
+model.exportPart(Part_2_doc, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterCurrent_1.py b/src/ExchangePlugin/Test/TestImportPart_AfterCurrent_1.py
new file mode 100644 (file)
index 0000000..0d104a8
--- /dev/null
@@ -0,0 +1,113 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+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)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/YOZ"), model.selection("EDGE", "PartSet/OY"), 45)
+model.do()
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# store the reference data
+features = Part_1_doc.allFeatures()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export all features from Part_1
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export features from {}".format(Part_1.name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1")], model.selection("EDGE", "PartSet/OX"), 50)
+model.end()
+
+# store features before the import
+featuresBeforeImportBegin = Part_1_doc.allFeatures()
+featuresBeforeImportBegin.pop_back() # remove Translation_1
+featuresBeforeImportBegin.pop_back() # remove Extrusion_1
+featuresBeforeImportFinish = [Translation_1.feature(), Extrusion_1.feature()]
+
+# import the document after Sketch_1
+model.begin()
+model.importPart(Part_1_doc, filename, Sketch_1)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImportBegin:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+for feat in featuresBeforeImportFinish:
+    if features.back().getKind() == feat.getKind() and features.back().name() == feat.name():
+        features.pop_back()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterCurrent_2.py b/src/ExchangePlugin/Test/TestImportPart_AfterCurrent_2.py
new file mode 100644 (file)
index 0000000..3f75cb3
--- /dev/null
@@ -0,0 +1,112 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+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)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/YOZ"), model.selection("EDGE", "PartSet/OY"), 45)
+model.do()
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# store the reference data
+features = Part_1_doc.allFeatures()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export all features from Part_1
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export features from {}".format(Part_1.name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1")], model.selection("EDGE", "PartSet/OX"), 50)
+model.end()
+
+# store features before the import
+featuresBeforeImportBegin = Part_1_doc.allFeatures()
+featuresBeforeImportBegin.pop_back() # remove Translation_1
+featuresBeforeImportFinish = [Translation_1.feature()]
+
+# import the document after Extrusion_1
+model.begin()
+model.importPart(Part_1_doc, filename, Extrusion_1)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImportBegin:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+for feat in featuresBeforeImportFinish:
+    if features.back().getKind() == feat.getKind() and features.back().name() == feat.name():
+        features.pop_back()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterLast_1.py b/src/ExchangePlugin/Test/TestImportPart_AfterLast_1.py
new file mode 100644 (file)
index 0000000..dd465a4
--- /dev/null
@@ -0,0 +1,137 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# store the reference data
+features = [Point_2.feature(), Axis_4.feature()]
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export feature from PartSet
+featureToExport = Axis_4
+model.begin()
+model.exportPart(partSet, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+model.end()
+
+# store features before the import
+featuresBeforeImport = Part_1_doc.allFeatures()
+
+# import the document
+model.begin()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImport:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterLast_2.py b/src/ExchangePlugin/Test/TestImportPart_AfterLast_2.py
new file mode 100644 (file)
index 0000000..218612e
--- /dev/null
@@ -0,0 +1,142 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Sketch_1
+
+# store the reference data
+sketch = featureToCompositeFeature(featureToExport.feature())
+features = [sketch]
+for i in range(0, sketch.numberOfSubs()):
+    features.append(sketch.subFeature(i))
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export sketch from Part_1
+model.begin()
+model.exportPart(Part_1_doc, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+model.end()
+
+# store features before the import
+featuresBeforeImport = Part_1_doc.allFeatures()
+
+# import the document
+model.begin()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImport:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterLast_3.py b/src/ExchangePlugin/Test/TestImportPart_AfterLast_3.py
new file mode 100644 (file)
index 0000000..f3f4c10
--- /dev/null
@@ -0,0 +1,138 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Box_1
+
+# store the reference data
+features = [featureToExport.feature()]
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export feature from Part_2
+model.begin()
+model.exportPart(Part_2_doc, filename, [featureToExport.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+model.end()
+
+# store features before the import
+featuresBeforeImport = Part_1_doc.allFeatures()
+
+# import the document
+model.begin()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImport:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterLast_4.py b/src/ExchangePlugin/Test/TestImportPart_AfterLast_4.py
new file mode 100644 (file)
index 0000000..2116733
--- /dev/null
@@ -0,0 +1,138 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+model.do()
+
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+featureToExport = Translation_1
+
+# store the reference data
+features = [Box_1.feature(), Translation_1.feature()]
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export all features from Part_2
+model.begin()
+model.exportPart(Part_2_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export feature {}".format(featureToExport.name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+model.end()
+
+# store features before the import
+featuresBeforeImport = Part_1_doc.allFeatures()
+
+# import the document
+model.begin()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImport:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterLast_5.py b/src/ExchangePlugin/Test/TestImportPart_AfterLast_5.py
new file mode 100644 (file)
index 0000000..1ad3b03
--- /dev/null
@@ -0,0 +1,137 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+Plane_4 = model.addPlane(Part_2_doc, model.selection("FACE", "PartSet/YOZ"), model.selection("EDGE", "PartSet/OY"), 45)
+model.do()
+
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# store the reference data
+features = [Box_1.feature(), Plane_4.feature()]
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export specified features from Part_2
+model.begin()
+model.exportPart(Part_2_doc, filename, [Box_1.result(), Plane_4.result()])
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export features {} and {}".format(feature[0].name(), features[1].name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+model.end()
+
+# store features before the import
+featuresBeforeImport = Part_1_doc.allFeatures()
+
+# import the document
+model.begin()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImport:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_AfterLast_6.py b/src/ExchangePlugin/Test/TestImportPart_AfterLast_6.py
new file mode 100644 (file)
index 0000000..58dac78
--- /dev/null
@@ -0,0 +1,138 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Point_2 = model.addPoint(partSet, 100, 100, 100)
+Axis_4 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "PartSet/Axis_4"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_1 = model.addBox(Part_2_doc, 10, 10, 10)
+Translation_1 = model.addTranslation(Part_2_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+Plane_4 = model.addPlane(Part_2_doc, model.selection("FACE", "PartSet/YOZ"), model.selection("EDGE", "PartSet/OY"), 45)
+model.do()
+
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# store the reference data
+features = [Box_1.feature(), Translation_1.feature()]
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+# export all features before the history line from Part_2
+model.begin()
+Part_2_doc.setCurrentFeature(Translation_1.feature(), False)
+model.exportPart(Part_2_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export features {} and {}".format(feature[0].name(), features[1].name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+model.end()
+
+# store features before the import
+featuresBeforeImport = Part_1_doc.allFeatures()
+
+# import the document
+model.begin()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+for feat in featuresBeforeImport:
+    if features.front().getKind() == feat.getKind() and features.front().name() == feat.name():
+        features.pop_front()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_Construction_1.py b/src/ExchangePlugin/Test/TestImportPart_Construction_1.py
new file mode 100644 (file)
index 0000000..45b0529
--- /dev/null
@@ -0,0 +1,82 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+# PartSet => PartSet
+
+model.begin()
+partSet = model.moduleDocument()
+Point_1 = model.addPoint(partSet, 100, 100, 100)
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Axis_1 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+model.end()
+
+# store the reference data
+features = partSet.allFeatures()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# emport the document
+model.begin()
+model.exportPart(partSet, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export PartSet"
+
+# close all documents
+model.reset()
+
+# import the document
+model.begin()
+partSet = model.moduleDocument()
+model.importPart(partSet, filename)
+model.end()
+
+# Test 1. No Part should be created
+assert(partSet.size(ModelAPI_ResultPart.group()) == 0)
+
+# Test 2. Compare results with the reference data
+TOLERANCE = 1.e-7
+features = partSet.allFeatures()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_Construction_2.py b/src/ExchangePlugin/Test/TestImportPart_Construction_2.py
new file mode 100644 (file)
index 0000000..bf0db67
--- /dev/null
@@ -0,0 +1,86 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+# PartSet => Part
+
+model.begin()
+partSet = model.moduleDocument()
+Point_1 = model.addPoint(partSet, 100, 100, 100)
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Axis_1 = model.addAxis(partSet, model.selection("VERTEX", "Origin"), model.selection("VERTEX", "Point_2"))
+model.end()
+
+# store the reference data (without Origin, coordinate axes and planes)
+features = partSet.allFeatures()
+for i in range(0, 7):
+    features.pop_front()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# emport the document
+model.begin()
+model.exportPart(partSet, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export PartSet"
+
+# close all documents
+model.reset()
+
+# import the document
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# Test 1. No Part should be created
+assert(partSet.size(ModelAPI_ResultPart.group()) == 1)
+
+# Test 2. Compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_Construction_3.py b/src/ExchangePlugin/Test/TestImportPart_Construction_3.py
new file mode 100644 (file)
index 0000000..5a84bd9
--- /dev/null
@@ -0,0 +1,87 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+# Part => PartSet
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Point_1 = model.addPoint(Part_1_doc, 100, 100, 100)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Axis_1 = model.addAxis(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("VERTEX", "Point_1"))
+model.end()
+
+# store the reference data
+features = Part_1_doc.allFeatures()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# emport the document
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export {}".format(Part_1.name())
+
+# close all documents
+model.reset()
+
+# import the document
+model.begin()
+partSet = model.moduleDocument()
+model.importPart(partSet, filename)
+model.end()
+
+# Test 1. No new Part should be created
+assert(partSet.size(ModelAPI_ResultPart.group()) == 0)
+
+# Test 2. Compare results with the reference data
+TOLERANCE = 1.e-7
+features = partSet.allFeatures()
+for i in range(0, 7):
+    # exclude Origin, coordinate axes and planes
+    features.pop_front()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_Construction_4.py b/src/ExchangePlugin/Test/TestImportPart_Construction_4.py
new file mode 100644 (file)
index 0000000..ae3fa13
--- /dev/null
@@ -0,0 +1,86 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+# Part => Part
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Point_1 = model.addPoint(Part_1_doc, 100, 100, 100)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Axis_1 = model.addAxis(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("VERTEX", "Point_1"))
+model.end()
+
+# store the reference data
+features = Part_1_doc.allFeatures()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# emport the document
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export {}".format(Part_1.name())
+
+# close all documents
+model.reset()
+
+# import the document
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# Test 1. No new Part should be created
+assert(partSet.size(ModelAPI_ResultPart.group()) == 1)
+
+# Test 2. Compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_Multiple.py b/src/ExchangePlugin/Test/TestImportPart_Multiple.py
new file mode 100644 (file)
index 0000000..7fdbc7e
--- /dev/null
@@ -0,0 +1,115 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+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)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+Translation_1.setName("MovedBox")
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/YOZ"), model.selection("EDGE", "PartSet/OY"), 45)
+model.do()
+model.end()
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# export all features from Part_1
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export features from {}".format(Part_1.name())
+
+# close all documents
+model.reset()
+
+# create new Part
+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"))
+SketchLine_1 = Sketch_1.addLine(-20, -40, -50, -40)
+SketchLine_2 = Sketch_1.addLine(-50, -40, -50, -10)
+SketchLine_3 = Sketch_1.addLine(-50, -10, -20, -10)
+SketchLine_4 = Sketch_1.addLine(-20, -10, -20, -40)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_1.result(), SketchLine_2.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 30)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistanceHorizontal_1 = Sketch_1.setHorizontalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 20)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_3.endPoint(), SketchAPI_Point(SketchPoint_1).coordinates(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4r-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 30, 0)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1")], model.selection("EDGE", "PartSet/OX"), 50)
+model.end()
+
+
+def checkUniqueNames(theDocument):
+    features = theDocument.allObjects()
+    names = set()
+    for feat in features:
+        name = feat.data().name()
+        if not name == "":
+            assert(not name in names), "'{}' already exists in {}".format(name, names)
+            names.add(name)
+
+
+# import the document at the end
+model.begin()
+model.importPart(Part_1_doc, filename)
+model.end()
+checkUniqueNames(Part_1_doc)
+
+# import the document after Extrusion_1
+model.begin()
+model.importPart(Part_1_doc, filename, Extrusion_1)
+model.end()
+checkUniqueNames(Part_1_doc)
+
+# import the document after Sketch_1
+model.begin()
+model.importPart(Part_1_doc, filename, Sketch_1)
+model.end()
+checkUniqueNames(Part_1_doc)
+
+# import the document after Translation_1
+model.begin()
+model.importPart(Part_1_doc, filename, Translation_1)
+model.end()
+checkUniqueNames(Part_1_doc)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_ToEmptyPart.py b/src/ExchangePlugin/Test/TestImportPart_ToEmptyPart.py
new file mode 100644 (file)
index 0000000..2df7249
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Point_1 = model.addPoint(Part_1_doc, 100, 100, 100)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Axis_1 = model.addAxis(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("VERTEX", "Point_1"))
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "Axis_1"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+model.end()
+
+# store the reference data
+features = Part_1_doc.allFeatures()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# emport the document
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export {}".format(Part_1.name())
+
+# close all documents
+model.reset()
+
+# import the document
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+model.importPart(Part_1_doc, filename)
+model.end()
+
+# Test 1. No new Part should be created
+assert(partSet.size(ModelAPI_ResultPart.group()) == 1)
+
+# Test 2. Compare results with the reference data
+TOLERANCE = 1.e-7
+features = Part_1_doc.allFeatures()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/Test/TestImportPart_ToEmptyPartSet.py b/src/ExchangePlugin/Test/TestImportPart_ToEmptyPartSet.py
new file mode 100644 (file)
index 0000000..0f0fee8
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright (C) 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 GeomAlgoAPI import *
+from ModelAPI import *
+from SketchAPI import *
+from salome.shaper import model
+
+import os
+import math
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Point_1 = model.addPoint(Part_1_doc, 100, 100, 100)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchCircle_1 = Sketch_1.addCircle(0, 0, 30)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchPoint_1.result(), SketchCircle_1.center())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 30)
+model.do()
+Axis_1 = model.addAxis(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("VERTEX", "Point_1"))
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection("EDGE", "Axis_1"), 100, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/To_Face"))
+SketchLine_1 = Sketch_2.addLine(57.73502691896258, 57.73502691896258, 71.87716254269353, 43.59289129523163)
+SketchLine_2 = Sketch_2.addLine(71.87716254269353, 43.59289129523163, 71.87716254269353, 71.87716254269353)
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_2.addLine(71.87716254269353, 71.87716254269353, 57.73502691896258, 57.73502691896258)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchConstraintPerpendicular_1 = Sketch_2.setPerpendicular(SketchLine_1.result(), SketchLine_3.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_1.result(), SketchLine_3.result())
+SketchProjection_2 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchCircle_1_2][Extrusion_1_1/To_Face]__cc"), False)
+SketchPoint_2 = SketchProjection_2.createdFeature()
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_2).coordinates(), SketchLine_1.startPoint())
+SketchConstraintLength_1 = Sketch_2.setLength(SketchLine_1.result(), 20)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f")], model.selection("EDGE", "PartSet/OX"), 180, 0)
+model.do()
+model.end()
+
+# store the reference data
+features = Part_1_doc.allFeatures()
+refData = []
+for feat in features:
+    res = []
+    for r in feat.results():
+        res.append(GeomAlgoAPI_ShapeTools.volume(r.shape()))
+    refData.append( (feat.getKind(), res) )
+
+filename = 'check_export.shaperpart'
+model.removeFile(filename)
+
+# emport the document
+model.begin()
+model.exportPart(Part_1_doc, filename)
+model.end()
+assert os.path.exists(filename), "ERROR: Cannot export {}".format(Part_1.name())
+
+# close all documents
+model.reset()
+
+# import the document
+model.begin()
+partSet = model.moduleDocument()
+model.importPart(partSet, filename)
+model.end()
+
+# Test 1. New Part should be created
+assert(partSet.size(ModelAPI_ResultPart.group()) == 1)
+newPart = modelAPI_ResultPart(objectToResult(partSet.object(ModelAPI_ResultPart.group(), 0)))
+newPart_doc = newPart.partDoc()
+
+# Test 2. Compare results with the reference data
+TOLERANCE = 1.e-7
+features = newPart_doc.allFeatures()
+assert(len(features) == len(refData))
+for feat, ref in zip(features, refData):
+    assert(feat.getKind() == ref[0])
+    for fv, rv in zip(feat.results(), ref[1]):
+        assert(math.fabs(GeomAlgoAPI_ShapeTools.volume(fv.shape()) - rv) < TOLERANCE)
+
+assert(model.checkPythonDump())
diff --git a/src/ExchangePlugin/icons/export_part.png b/src/ExchangePlugin/icons/export_part.png
new file mode 100644 (file)
index 0000000..98e423e
Binary files /dev/null and b/src/ExchangePlugin/icons/export_part.png differ
diff --git a/src/ExchangePlugin/icons/import_part.png b/src/ExchangePlugin/icons/import_part.png
new file mode 100644 (file)
index 0000000..8443bd1
Binary files /dev/null and b/src/ExchangePlugin/icons/import_part.png differ
index 59a246ecdcbe081fbe1eaa540f736ffad1039893..b2da9c048c13a88bf0b6d8cf40d3f5eef47a1f44 100644 (file)
@@ -12,7 +12,7 @@
         <source path="export_widget.xml" />
       </feature>
       <feature id="Dump" title="Dump" tooltip="Dump Python script" icon="icons/Exchange/dump.png"
-               helpfile="dumpFeature.html">
+               helpfile="dumpFeature.html" abort_confirmation="false">
         <export_file_selector id="file_path"
                               type="save"
                               title="Dump to file"
              tooltip="To use geometrical order for identification of selected shapes"
              default="false"/> -->
       </feature>
+
+      <feature id="ImportPart" title="Import part" tooltip="Import features from file" icon="icons/Exchange/import_part.png"
+               helpfile="importPart.html"
+               internal="1">
+        <file_selector id="file_path" title="Import file" path="">
+          <validator id="ExchangePlugin_ImportFormat" parameters="shaperpart:Part" />
+        </file_selector>
+      </feature>
+      <feature id="ExportPart" title="Export part" tooltip="Export structure of the Part to file" icon="icons/Exchange/export_part.png"
+               helpfile="exportPart.html"
+               internal="1">
+        <export_file_selector id="file_path"
+                              type="save"
+                              title="Export file"
+                              path="">
+          <validator id="ExchangePlugin_ExportFormat"
+                     parameters="shaperpart:Part" />
+        </export_file_selector>
+        <multi_selector id="selection_list"
+                        tooltip="Select features or results"
+                        shape_types="Vertices Edges Faces Solids Compsolids Objects">
+        </multi_selector>
+      </feature>
     </group>
   </workbench>
 </plugin>
\ No newline at end of file
index 9fccd0f081b8bb658a857dc494db4b09db7eed40..04194d5b10c1acd26516246a8254efd8b3ca2857 100644 (file)
@@ -113,9 +113,10 @@ void FeaturesPlugin_BooleanFuse::execute()
 
   // Collecting solids from compsolids which will not be modified
   // in boolean operation and will be added to result.
+  bool isProcessCompsolid = !isSimpleCreation || aFuseVersion >= THE_FUSE_VERSION_1;
   ListOfShape aShapesToAdd;
   for (ObjectHierarchy::Iterator anObjectsIt = anObjectsHierarchy.Begin();
-       !isSimpleCreation && anObjectsIt != anObjectsHierarchy.End();
+       isProcessCompsolid && anObjectsIt != anObjectsHierarchy.End();
        ++anObjectsIt) {
     GeomShapePtr anObject = *anObjectsIt;
     GeomShapePtr aParent = anObjectsHierarchy.Parent(anObject, false);
@@ -144,8 +145,11 @@ void FeaturesPlugin_BooleanFuse::execute()
       aMakeShapeList->appendAlgo(aCutAlgo);
     }
   }
-  anOriginalShapes.insert(anOriginalShapes.end(), anEdgesAndFaces.begin(),
-                          anEdgesAndFaces.end());
+
+  if (aShapesToAdd.empty() || !aCuttedEdgesAndFaces) {
+    anOriginalShapes.insert(anOriginalShapes.end(), anEdgesAndFaces.begin(),
+                            anEdgesAndFaces.end());
+  }
 
   // If we have compsolids then cut with not used solids all others.
   if (!aShapesToAdd.empty()) {
index 44de21fb5ffc40aa01ab76d7432e6a4a2bf01a98..07db4dd86f32d32371a2dede20b1e61811bde22f 100644 (file)
@@ -1742,9 +1742,6 @@ bool FeaturesPlugin_ValidatorBooleanFuseArguments::isValid(
   if (anObjectsNb + aToolsNb < 2) {
     theError = "Not enough arguments for Fuse operation.";
     return false;
-  } else if (isAllInSameCompSolid) {
-    theError = "Operations only between sub-shapes of the same shape not allowed.";
-    return false;
   }
 
   return true;
index 6ed38227224ad719726a55562dff460d5a878c58..ee29a96b32cb8ea0c421b6164c648bbd5bcbc11f 100644 (file)
@@ -60,6 +60,12 @@ model.testNbSubShapes(Fuse_1, GeomAPI_Shape.VERTEX, [32])
 model.testResultsVolumes(Fuse_1, [785.398163397447774514148477465])
 
 Fuse_2 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Extrusion_1_1_3"), model.selection("SOLID", "Extrusion_1_1_1")], [model.selection("FACE", "Fuse_1_1_1")])
-assert(Fuse_2.feature().error() != "")
 
+model.testNbResults(Fuse_2, 1)
+model.testNbSubResults(Fuse_2, [2])
+model.testNbSubShapes(Fuse_2, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Fuse_2, GeomAPI_Shape.FACE, [19])
+model.testNbSubShapes(Fuse_2, GeomAPI_Shape.EDGE, [82])
+model.testNbSubShapes(Fuse_2, GeomAPI_Shape.VERTEX, [164])
+model.testResultsVolumes(Fuse_2, [25803.607097738855372881516814232])
 model.end()
index d69e7931494a687e747740b268920875aceca73c..b90c2f27624ef3cf1cbac1e5d8aab1728447c81d 100644 (file)
@@ -31,11 +31,13 @@ assert(Fuse_1.feature().error() != "")
 Part_1_doc.removeFeature(Fuse_1.feature())
 
 Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Partition_1_1_1"), model.selection("SOLID", "Partition_1_1_2")])
-assert(Fuse_1.feature().error() != "")
+# after merging Union and Fuse features, fusing of solids in the same composolid should work (issue #3062)
+assert(Fuse_1.feature().error() == "")
 Part_1_doc.removeFeature(Fuse_1.feature())
 
 Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Partition_1_1_1")], [model.selection("SOLID", "Partition_1_1_2")])
-assert(Fuse_1.feature().error() != "")
+# after merging Union and Fuse features, fusing of solids in the same composolid should work (issue #3062)
+assert(Fuse_1.feature().error() == "")
 Part_1_doc.removeFeature(Fuse_1.feature())
 model.end()
 
index 87e7b0c90571ccbcfbca6aefbb1b52e003b851da..ce912b897f573cb18f6ed18704bb5ae0482e2642 100644 (file)
@@ -39,7 +39,7 @@ model.end()
 from GeomAPI import  GeomAPI_Shape
 
 model.testNbResults(Fuse_1, 1)
-model.testNbSubResults(Fuse_1, [1])
+model.testNbSubResults(Fuse_1, [0])
 model.testNbSubShapes(Fuse_1, GeomAPI_Shape.SOLID, [0])
 model.testNbSubShapes(Fuse_1, GeomAPI_Shape.FACE, [1])
 model.testNbSubShapes(Fuse_1, GeomAPI_Shape.EDGE, [2])
index 64de0bacc28028fc518661fe1cf6f9c9c7ce3982..c6ba22f70ed70ff444f971aa7b89cb00019d25a9 100644 (file)
@@ -78,4 +78,12 @@ Compound_1 = model.addCompound(Part_1_doc, [model.selection("COMPSOLID", "Extrus
 Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Compound_1_1_1_1")], [model.selection("SOLID", "LinearCopy_2_1_1_1"), model.selection("FACE", "Compound_1_1_2")], True)
 model.end()
 
-assert(Fuse_1.feature().error() != "")
+from GeomAPI import *
+
+model.testNbResults(Fuse_1, 1)
+model.testNbSubResults(Fuse_1, [2])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.FACE, [16])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.EDGE, [78])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.VERTEX, [156])
+model.testResultsVolumes(Fuse_1, [1589.048622670478835])
index 25eb78b79e4d65ec85ee6cf9cccc8caa068a6503..77fb3065759ad460a1f7f8233bdc0657f83529b9 100644 (file)
@@ -85,11 +85,11 @@ model.end()
 from GeomAPI import GeomAPI_Shape
 
 model.testNbResults(Fuse_1, 1)
-model.testNbSubResults(Fuse_1, [2])
-model.testNbSubShapes(Fuse_1, GeomAPI_Shape.SOLID, [6])
-model.testNbSubShapes(Fuse_1, GeomAPI_Shape.FACE, [23])
-model.testNbSubShapes(Fuse_1, GeomAPI_Shape.EDGE, [70])
-model.testNbSubShapes(Fuse_1, GeomAPI_Shape.VERTEX, [140])
-model.testResultsVolumes(Fuse_1, [5016.039439659862])
+model.testNbSubResults(Fuse_1, [3])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.SOLID, [7])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.FACE, [31])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.EDGE, [108])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.VERTEX, [216])
+model.testResultsVolumes(Fuse_1, [5516.039439659862])
 
 assert(model.checkPythonDump())
index 23e7c75afc9348e7f545079eaadc55c2b2f2f003..57226b3e3b367f24e94628bd3b02429c5a1a7bd2 100644 (file)
@@ -78,4 +78,12 @@ Compound_1 = model.addCompound(Part_1_doc, [model.selection("COMPSOLID", "Extrus
 Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Compound_1_1_1_1")], [model.selection("SOLID", "LinearCopy_2_1_1_1"), model.selection("FACE", "Compound_1_1_2")], True, 20190506)
 model.end()
 
-assert(Fuse_1.feature().error() != "")
+from GeomAPI import *
+
+model.testNbResults(Fuse_1, 1)
+model.testNbSubResults(Fuse_1, [5])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.SOLID, [9])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.FACE, [44])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.EDGE, [160])
+model.testNbSubShapes(Fuse_1, GeomAPI_Shape.VERTEX, [320])
+model.testResultsVolumes(Fuse_1, [6516.03943965772123])
index ccb6e644b85c7df2cad239ee2bb2cdced28beb74..14b0973da9b0808e3823208b4dd305c12b9000d2 100644 (file)
@@ -84,10 +84,12 @@ anExtrusionResult = modelAPI_ResultBody(anExtrusionFeature.firstResult())
 # Make union on extrusion
 #=========================================================================
 aSession.startOperation()
-aUnionFeature = aPart.addFeature("Union")
-aUnionFeature.selectionList("base_objects").append(anExtrusionResult.subResult(0), None);
-aUnionFeature.selectionList("base_objects").append(anExtrusionResult.subResult(1), None);
-aUnionFeature.selectionList("base_objects").append(anExtrusionResult.subResult(2), None);
+aUnionFeature = aPart.addFeature("Fuse")
+aUnionFeature.string("creation_method").setValue("simple")
+aUnionFeature.selectionList("main_objects").append(anExtrusionResult.subResult(0), None);
+aUnionFeature.selectionList("main_objects").append(anExtrusionResult.subResult(1), None);
+aUnionFeature.selectionList("main_objects").append(anExtrusionResult.subResult(2), None);
+aUnionFeature.boolean("remove_intersection_edges").setValue(False)
 aSession.finishOperation()
 assert (len(aUnionFeature.results()) > 0)
 anUnionResult = modelAPI_ResultBody(aUnionFeature.firstResult())
index fbcf82f5256d9866af98b726a341e05bf24a20ae..f2b650ec86521cf2a8ea077ce9311d70281cdc2a 100644 (file)
@@ -37,7 +37,7 @@ model.do()
 Edge_1 = model.addEdge(Part_1_doc, [model.selection("EDGE", "Sketch_1/SketchArc_1_2")])
 Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("EDGE", "Edge_1_1")], model.selection("EDGE", "PartSet/OY"), 200, 0)
 Partition_1 = model.addPartition(Part_1_doc, [model.selection("FACE", "PartSet/XOZ"), model.selection("FACE", "Revolution_1_1")])
-Union_1 = model.addUnion(Part_1_doc, [model.selection("FACE", "Partition_1_1_1"), model.selection("FACE", "Partition_1_1_2")])
+Union_1 = model.addFuse(Part_1_doc, [model.selection("FACE", "Partition_1_1_1"), model.selection("FACE", "Partition_1_1_2")], True)
 model.do()
 
 model.checkResult(Union_1,model,1,[0],[0],[1],[4],[8])
index 8679aeb10773f4647f398d539825c2f4f21c678c..c1082408cf4d30b1fdc0d68551901cd6905e7532 100644 (file)
@@ -39,7 +39,7 @@ Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("EDGE", "Edge_1_
 Partition_1_objects = [model.selection("FACE", "PartSet/YOZ"), model.selection("FACE", "PartSet/XOZ"), model.selection("FACE", "PartSet/XOY"), model.selection("FACE", "Revolution_1_1")]
 Partition_1 = model.addPartition(Part_1_doc, Partition_1_objects)
 Union_1_objects = [model.selection("FACE", "Partition_1_1_1"), model.selection("FACE", "Partition_1_1_2"), model.selection("FACE", "Partition_1_1_3"), model.selection("FACE", "Partition_1_1_4")]
-Union_1 = model.addUnion(Part_1_doc, Union_1_objects)
+Union_1 = model.addFuse(Part_1_doc, Union_1_objects, True)
 model.testHaveNamingSubshapes(Union_1,model,Part_1_doc)
 model.do()
 model.end()
index 0f43467578b3154f770d196f66c73237d55b107a..a3772deae41d6fda4b9ff7a711ac4f6c888a24a2 100644 (file)
@@ -57,11 +57,11 @@ Face_2 = model.addFace(Part_1_doc, Face_2_objects)
 Partition_1_objects = [model.selection("FACE", "Face_2_1"), model.selection("FACE", "Face_1_1"), model.selection("FACE", "PartSet/YOZ")]
 Partition_1 = model.addPartition(Part_1_doc, Partition_1_objects)
 Union_1_objects = [model.selection("FACE", "Partition_1_1_1"), model.selection("FACE", "Partition_1_1_3"), model.selection("FACE", "Partition_1_1_4")]
-Union_1 = model.addUnion(Part_1_doc, Union_1_objects)
+Union_1 = model.addFuse(Part_1_doc, Union_1_objects, True, 20190506)
 model.do()
 
 model.checkResult(Union_1,model,1,[2],[0],[2],[13],[26])
-#model.testHaveNamingSubshapes(Union_1,model,Part_1_doc)
+model.testHaveNamingSubshapes(Union_1,model,Part_1_doc)
 
 model.end()
 
index 6d44cb3dcdc42ddd07332e8ab7d44c252edbf86c..39ff6a3cde07c6386c0802cdd0db36e2e24e809b 100644 (file)
@@ -28,8 +28,8 @@ Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "Box_1_1/Left"), 5,
 Plane_5 = model.addPlane(Part_1_doc, model.selection("FACE", "Box_1_1/Front"), 5, True)
 Partition_1_objects = [model.selection("FACE", "Plane_1"), model.selection("FACE", "Plane_2"), model.selection("SOLID", "Box_1_1")]
 Partition_1 = model.addPartition(Part_1_doc, Partition_1_objects)
-Union_1 = model.addUnion(Part_1_doc, [model.selection("SOLID", "Partition_1_1_3"), model.selection("SOLID", "Partition_1_1_1")])
-Union_2 = model.addUnion(Part_1_doc, [model.selection("SOLID", "Union_1_1_2"), model.selection("SOLID", "Union_1_1_1")])
+Union_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Partition_1_1_3"), model.selection("SOLID", "Partition_1_1_1")], False, 20190506)
+Union_2 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Fuse_1_1_2"), model.selection("SOLID", "Fuse_1_1_1")], False, 20190506)
 model.do()
 model.end()
 
index 65c1a1e45998eb49143b4a11e0321efc1f02a32c..c902ef0048630747e9d2aa96985ea9b3f8d5c9ed 100644 (file)
@@ -81,7 +81,7 @@
           <source path="boolean_split_widget.xml"/>
       </feature>
       <feature id="Union" title="Union" tooltip="Perform union operations with shapes"
-               icon="icons/Features/union.png" helpfile="unionFeature.html">
+               icon="icons/Features/union.png" helpfile="unionFeature.html" internal="1">
           <source path="union_widget.xml"/>
       </feature>
       <feature id="Remove_SubShapes" title="Remove Sub-Shapes" tooltip="Allows to remove sub-shapes from wires, shells, compsolids and compounds"
     </group>
     <group id="Measurement">
       <feature id="Measurement" title="Measurement" tooltip="Calculate properties of objects"
-               icon="icons/Features/measurement.png" helpfile="measurementFeature.html">
+               icon="icons/Features/measurement.png" helpfile="measurementFeature.html" abort_confirmation="false">
         <source path="measurement_widget.xml"/>
       </feature>
     </group>
index c33177b73d961182d402d189ed6032270f9c3203..1aeb1140f60800294cb513ad202693e5c93cbcb6 100644 (file)
@@ -20,6 +20,7 @@
 #include "GeomAlgoAPI_UnifySameDomain.h"
 
 #include <GeomAlgoAPI_CompoundBuilder.h>
+#include <GeomAlgoAPI_DFLoader.h>
 #include <GeomAlgoAPI_ShapeTools.h>
 
 #include <ShapeUpgrade_UnifySameDomain.hxx>
@@ -113,6 +114,10 @@ void GeomAlgoAPI_UnifySameDomain::build(const GeomShapePtr& theShape,
   if (aResult.IsNull()) {
     return;
   }
+  // taske off the compound if it consists of single sub-shape
+  if (aResult.ShapeType() == TopAbs_COMPOUND) {
+    aResult = GeomAlgoAPI_DFLoader::refineResult(aResult);
+  }
 
   if (theIsToSimplifyShell && aResult.ShapeType() == TopAbs_SHELL) {
     int aNb = 0;
index d03030e359c845a8f83594f45ff13137bde423ec..23c7ce1c4141c879640569ab080848dd5b811f93 100644 (file)
@@ -79,5 +79,6 @@ void GeomData_Dir::reinit()
   if (!myIsInitialized) {
     // create attribute: not initialized by value yet, just zero
     myCoords = TDataStd_RealArray::Set(myLab, 0, 2);
+    myIsInitialized = true;
   }
 }
index 554b8c03a56b0d03c8040699d627ca1f5043565b..eaac6b906a332e4eddcde3e7621f0c4de4a8d630 100644 (file)
@@ -53,6 +53,7 @@ SET(PROJECT_HEADERS
     Model_ResultField.h
     Model_ResultGroup.h
     Model_ResultParameter.h
+    Model_Tools.h
     Model_Update.h
     Model_Validator.h
 )
@@ -90,6 +91,7 @@ SET(PROJECT_SOURCES
     Model_ResultField.cpp
     Model_ResultGroup.cpp
     Model_ResultParameter.cpp
+    Model_Tools.cpp
     Model_Update.cpp
     Model_Validator.cpp
 )
@@ -115,6 +117,7 @@ SET(PROJECT_INCLUDES
   ../GeomAlgoAPI
   ../GeomAPI
   ../ModelGeomAlgo
+  ../ConstructionPlugin
   ${OpenCASCADE_INCLUDE_DIR}
 )
 
index 6cfb6a3a38db0fce52b852cab4547a685ca39aaf..9dcb0bd55bf54245f19e73b059ba1ac7027a8dff 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <ModelAPI_Events.h>
 
+#include <PCDM_RetrievalDriver.hxx>
+
 IMPLEMENT_STANDARD_RTTIEXT(Model_Application, TDocStd_Application)
 
 static Handle_Model_Application TheApplication = new Model_Application;
@@ -168,6 +170,14 @@ Model_Application::Model_Application()
   // store handle to the application to avoid nullification
   static Handle(Model_Application) TheKeepHandle;
   TheKeepHandle = this;
+  // additional file format supported
+  static TCollection_ExtendedString THE_DOC_FORMAT("BinShaperPart");
+  static TCollection_ExtendedString THE_FILE_EXT("shaperpart");
+  Handle(PCDM_RetrievalDriver) aReader =
+      Handle(PCDM_RetrievalDriver)::DownCast(TheKeepHandle->ReaderFromFormat("BinOcaf"));
+  Handle(PCDM_StorageDriver) aWriter = TheKeepHandle->WriterFromFormat("BinOcaf");
+  TheKeepHandle->DefineFormat(THE_DOC_FORMAT, "Shaper Part document", THE_FILE_EXT,
+                              aReader, aWriter);
 }
 
 //=======================================================================
index 2a2f5fa6791fee09f7b3762587f1663bb988d611..0b2d2869a3ed7f120f5f1449c405a21d598d94d9 100644 (file)
@@ -125,6 +125,7 @@ void Model_AttributeReference::reinit()
   myIsInitialized = myLab.FindAttribute(TDF_Reference::GetID(), myRef) == Standard_True;
   if (!myIsInitialized) {
     myRef = TDF_Reference::Set(myLab, myLab);  // not initialized references to itself
+    myIsInitialized = true;
   } else {
     if (owner()) {
       std::shared_ptr<Model_Document> aDoc =
index 479df0e05a5df87db9023ba1aa40413c32035d3f..8ff6d81f8a87b93a8b4a402de30c454f7893cd8b 100644 (file)
@@ -302,7 +302,7 @@ GeomShapePtr centerByEdge(GeomShapePtr theEdge, ModelAPI_AttributeSelection::Cen
 
 std::shared_ptr<GeomAPI_Shape> Model_AttributeSelection::value()
 {
-  if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
+  if (!myRef.isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
     return std::shared_ptr<GeomAPI_Shape>();
   CenterType aType = NOT_CENTER;
   std::shared_ptr<GeomAPI_Shape> aResult = internalValue(aType);
@@ -421,32 +421,29 @@ bool Model_AttributeSelection::isInvalid()
 
 bool Model_AttributeSelection::isInitialized()
 {
-  if (ModelAPI_AttributeSelection::isInitialized()) { // additional checks if it is initialized
-    std::shared_ptr<GeomAPI_Shape> aResult;
-    if (myRef.isInitialized()) {
-      TDF_Label aSelLab = selectionLabel();
-      // it is just reference to shape, not sub-shape
-      if (aSelLab.IsAttribute(kSIMPLE_REF_ID) || aSelLab.IsAttribute(kPART_REF_ID)) {
-        ResultPtr aContext = context();
-        return aContext.get() != NULL;
-      }
-      Handle(TNaming_NamedShape) aSelection;
-      if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
-        return !aSelection->Get().IsNull();
-      } else { // for simple construction element: just shape of this construction element
-        if (myRef.value().get())
-          return true;
-        // check that this is on open of document, so, results are not initialized yet
-        TDF_Label aRefLab = myRef.myRef->Get();
-        if (aRefLab.IsNull() || !owner().get())
-          return false;
-        std::shared_ptr<Model_Document> aMyDoc =
-          std::dynamic_pointer_cast<Model_Document>(owner()->document());
-        if (!aMyDoc.get())
-          return false;
-        // check at least the feature exists
-        return aMyDoc->featureByLab(aRefLab).get() != NULL;
-      }
+  if (myRef.isInitialized()) {
+    TDF_Label aSelLab = selectionLabel();
+    // it is just reference to shape, not sub-shape
+    if (aSelLab.IsAttribute(kSIMPLE_REF_ID) || aSelLab.IsAttribute(kPART_REF_ID)) {
+      ResultPtr aContext = context();
+      return aContext.get() != NULL;
+    }
+    Handle(TNaming_NamedShape) aSelection;
+    if (selectionLabel().FindAttribute(TNaming_NamedShape::GetID(), aSelection)) {
+      return !aSelection->Get().IsNull();
+    } else { // for simple construction element: just shape of this construction element
+      if (myRef.value().get())
+        return true;
+      // check that this is on open of document, so, results are not initialized yet
+      TDF_Label aRefLab = myRef.myRef->Get();
+      if (aRefLab.IsNull() || !owner().get())
+        return false;
+      std::shared_ptr<Model_Document> aMyDoc =
+        std::dynamic_pointer_cast<Model_Document>(owner()->document());
+      if (!aMyDoc.get())
+        return false;
+      // check at least the feature exists
+      return aMyDoc->featureByLab(aRefLab).get() != NULL;
     }
   }
   return false;
@@ -476,7 +473,7 @@ void Model_AttributeSelection::setID(const std::string theID)
 
 ResultPtr Model_AttributeSelection::context()
 {
-  if (!ModelAPI_AttributeSelection::isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
+  if (!myRef.isInitialized() && !myTmpContext.get() && !myTmpSubShape.get())
     return ResultPtr();
 
   if (myTmpContext.get() || myTmpSubShape.get()) {
@@ -2034,3 +2031,9 @@ TDF_Label Model_AttributeSelection::baseDocumentLab()
   static TDF_Label anEmpty;
   return anEmpty;
 }
+
+void Model_AttributeSelection::reset()
+{
+  ModelAPI_AttributeSelection::reset();
+  myRef.reset();
+}
index bd26925b3fd229cac2ab96282b2d645a01fa4407..50a7ddbb91645726c340b095ce6db0ac97e3acd8 100644 (file)
@@ -143,6 +143,9 @@ public:
   /// Makes the current local selection becomes all sub-shapes with same base geometry.
   MODEL_EXPORT virtual void combineGeometrical();
 
+  /// Resets attribute to deafult state
+  MODEL_EXPORT virtual void reset();
+
 protected:
   /// Objects are created for features automatically
   MODEL_EXPORT Model_AttributeSelection(TDF_Label& theLabel);
index 10d11678d642420f5bde838baabe2072dce78114..45798a12fc36a93d98911e2e3cc0cfa6b8c0a38f 100644 (file)
@@ -35,6 +35,9 @@
 #include <Model_AttributeTables.h>
 #include <Model_Events.h>
 #include <Model_Expression.h>
+#include <Model_Tools.h>
+#include <Model_Validator.h>
+
 #include <ModelAPI_Feature.h>
 #include <ModelAPI_Result.h>
 #include <ModelAPI_ResultParameter.h>
@@ -43,7 +46,6 @@
 #include <ModelAPI_Session.h>
 #include <ModelAPI_ResultPart.h>
 #include <ModelAPI_Tools.h>
-#include <Model_Validator.h>
 
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Point2D.h>
 
 #include <TDataStd_Name.hxx>
 #include <TDataStd_AsciiString.hxx>
-#include <TDataStd_IntegerArray.hxx>
 #include <TDataStd_UAttribute.hxx>
-#include <TDF_AttributeIterator.hxx>
-#include <TDF_ChildIterator.hxx>
-#include <TDF_RelocationTable.hxx>
 #include <TDF_ChildIDIterator.hxx>
-#include <TColStd_HArray1OfByte.hxx>
 
 #include <string>
 
@@ -797,37 +794,10 @@ void Model_Data::referencesToObjects(
   }
 }
 
-/// makes copy of all attributes on the given label and all sub-labels
-static void copyAttrs(TDF_Label theSource, TDF_Label theDestination) {
-  TDF_AttributeIterator anAttrIter(theSource);
-  for(; anAttrIter.More(); anAttrIter.Next()) {
-    Handle(TDF_Attribute) aTargetAttr;
-    if (!theDestination.FindAttribute(anAttrIter.Value()->ID(), aTargetAttr)) {
-      // create a new attribute if not yet exists in the destination
-           aTargetAttr = anAttrIter.Value()->NewEmpty();
-      theDestination.AddAttribute(aTargetAttr);
-    }
-    // no special relocation, empty map, but self-relocation is on: copy references w/o changes
-    Handle(TDF_RelocationTable) aRelocTable = new TDF_RelocationTable(Standard_True);
-    anAttrIter.Value()->Paste(aTargetAttr, aRelocTable);
-    // an exception: if a source reference refers itself, a copy must also refer itself
-    if (aTargetAttr->ID() == TDF_Reference::GetID()) {
-      Handle(TDF_Reference) aTargetRef = Handle(TDF_Reference)::DownCast(aTargetAttr);
-      if (aTargetRef->Get().IsEqual(anAttrIter.Value()->Label()))
-        aTargetRef->Set(aTargetRef->Label());
-    }
-  }
-  // copy the sub-labels content
-  TDF_ChildIterator aSubLabsIter(theSource);
-  for(; aSubLabsIter.More(); aSubLabsIter.Next()) {
-    copyAttrs(aSubLabsIter.Value(), theDestination.FindChild(aSubLabsIter.Value().Tag()));
-  }
-}
-
 void Model_Data::copyTo(std::shared_ptr<ModelAPI_Data> theTarget)
 {
   TDF_Label aTargetRoot = std::dynamic_pointer_cast<Model_Data>(theTarget)->label();
-  copyAttrs(myLab, aTargetRoot);
+  Model_Tools::copyAttrs(myLab, aTargetRoot);
   // reinitialize Model_Attributes by TDF_Attributes set
   std::shared_ptr<Model_Data> aTData = std::dynamic_pointer_cast<Model_Data>(theTarget);
   aTData->myAttrs.clear();
index 9f26d8de1050bd4a25b85f481e00f243534c7906..7f9fa9b01584f120771ad4405ceaa1dfe24022bd 100644 (file)
@@ -96,6 +96,7 @@ class Model_Data : public ModelAPI_Data
   friend class Model_SelectionNaming;
   friend class Model_ResultConstruction;
   friend class Model_ResultBody;
+  friend class Model_Tools;
 
  public:
   /// The simplest constructor. "setLabel" must be called just after to initialize correctly.
index be2877848b1a34b891a39b3dae19561fb94e780a..2e01c4d9d8b78725ab7562da6c2e57235babe0cc 100644 (file)
@@ -23,6 +23,7 @@
 #include <Model_Application.h>
 #include <Model_Session.h>
 #include <Model_Events.h>
+#include <Model_Tools.h>
 #include <ModelAPI_ResultPart.h>
 #include <ModelAPI_Validator.h>
 #include <ModelAPI_CompositeFeature.h>
 #include <TNaming_Iterator.hxx>
 #include <TNaming_NamedShape.hxx>
 #include <TNaming_Tool.hxx>
-#include<TNaming_OldShapeIterator.hxx>
+#include <TNaming_OldShapeIterator.hxx>
 #include <TopTools_DataMapOfShapeShape.hxx>
 #include <TopTools_ListOfShape.hxx>
 
 #include <TopExp_Explorer.hxx>
 #include <TopoDS_Shape.hxx>
 
+#include <OSD_Directory.hxx>
 #include <OSD_File.hxx>
 #include <OSD_Path.hxx>
+#include <OSD_Protection.hxx>
 #include <CDF_Session.hxx>
 #include <CDF_Directory.hxx>
+#include <UTL.hxx>
 
 #include <climits>
 #ifndef WIN32
@@ -221,24 +225,20 @@ static void updateShapesFromRoot(const TDF_Label theThisAccess, const TDF_Label
 }
 // LCOV_EXCL_STOP
 
-bool Model_Document::load(const char* theDirName, const char* theFileName, DocumentPtr theThis)
+static bool loadDocument(Handle(Model_Application) theApp,
+                         Handle(TDocStd_Document)& theDoc,
+                         const TCollection_ExtendedString& theFilename)
 {
-  Handle(Model_Application) anApp = Model_Application::getApplication();
-  if (isRoot()) {
-    anApp->setLoadPath(theDirName);
-  }
-  TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName));
-  PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus) -1;
-  Handle(TDocStd_Document) aLoaded;
+  PCDM_ReaderStatus aStatus = (PCDM_ReaderStatus)-1;
   try {
-    aStatus = anApp->Open(aPath, aLoaded);
+    aStatus = theApp->Open(theFilename, theDoc);
   } catch (Standard_Failure const& anException) {
     Events_InfoMessage("Model_Document",
         "Exception in opening of document: %1").arg(anException.GetMessageString()).send();
     return false;
   }
-  bool isError = aStatus != PCDM_RS_OK;
-  if (isError) {
+  bool isOk = aStatus == PCDM_RS_OK;
+  if (!isOk) {
     // LCOV_EXCL_START
     switch (aStatus) {
       case PCDM_RS_UnknownDocument:
@@ -296,9 +296,22 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum
     }
     // LCOV_EXCL_STOP
   }
+  return isOk;
+}
+
+bool Model_Document::load(const char* theDirName, const char* theFileName, DocumentPtr theThis)
+{
+  Handle(Model_Application) anApp = Model_Application::getApplication();
+  if (isRoot()) {
+    anApp->setLoadPath(theDirName);
+  }
+  TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName));
+  Handle(TDocStd_Document) aLoaded;
+  bool isOk = loadDocument(anApp, aLoaded, aPath);
+
   std::shared_ptr<Model_Session> aSession =
     std::dynamic_pointer_cast<Model_Session>(Model_Session::get());
-  if (!isError) {
+  if (isOk) {
     myDoc = aLoaded;
     myDoc->SetUndoLimit(UNDO_LIMIT);
 
@@ -333,7 +346,124 @@ bool Model_Document::load(const char* theDirName, const char* theFileName, Docum
   } else { // open failed, but new document was created to work with it: inform the model
     aSession->setActiveDocument(Model_Session::get()->moduleDocument(), false);
   }
-  return !isError;
+  return isOk;
+}
+
+bool Model_Document::import(const char* theFileName,
+                            std::list<std::shared_ptr<ModelAPI_Feature> >& theImported,
+                            bool theCheckBefore)
+{
+  Handle(Model_Application) anApp = Model_Application::getApplication();
+  TCollection_ExtendedString aFormat;
+  if (!anApp->Format(theFileName, aFormat))
+    return false;
+
+  Handle(TDocStd_Document) aTempDoc;
+  bool isOk = loadDocument(anApp, aTempDoc, theFileName);
+
+  if (isOk && theCheckBefore) {
+    // verify all features are applicable for the current document type (e.g. PartSet)
+    std::shared_ptr<Model_Session> aSession =
+        std::dynamic_pointer_cast<Model_Session>(ModelAPI_Session::get());
+    for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More() && isOk; anIt.Next()) {
+      TDF_Label aCurrentLab = anIt.Value();
+      Handle(TDataStd_Comment) aFeatureID;
+      TDF_Label aNewFeatuerLab;
+      if (aCurrentLab.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) {
+        TCollection_AsciiString anID(aFeatureID->Get());
+        std::string aFeatureKind(anID.ToCString());
+        if (aSession->myPlugins.find(aFeatureKind) != aSession->myPlugins.end()) {
+          std::string& aDocKind = aSession->myPlugins[aFeatureKind].second;
+          isOk = aDocKind.empty() || aDocKind == kind();
+        }
+      }
+    }
+  }
+
+  if (isOk) {
+    // copy features from the temporary document to the current
+    Handle(TDF_RelocationTable) aRelocTable = new TDF_RelocationTable();
+    TDF_LabelList anAllNewFeatures;
+    // Perform the copying twice for correct references:
+    // 1. copy labels hierarchy and fill the relocation table
+    TDF_Label aMain = myDoc->Main();
+    for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More(); anIt.Next()) {
+      TDF_Label aCurrentLab = anIt.Value();
+      Handle(TDataStd_Comment) aFeatureID;
+      TDF_Label aNewFeatuerLab;
+      if (aCurrentLab.FindAttribute(TDataStd_Comment::GetID(), aFeatureID)) {
+        TCollection_AsciiString anID(aFeatureID->Get());
+        FeaturePtr aNewFeature = addFeature(anID.ToCString());
+        std::shared_ptr<Model_Data> aData =
+            std::dynamic_pointer_cast<Model_Data>(aNewFeature->data());
+        aNewFeatuerLab = aData->label().Father();
+        Model_Tools::copyLabels(aCurrentLab, aNewFeatuerLab, aRelocTable);
+        theImported.push_back(aNewFeature);
+      }
+      anAllNewFeatures.Append(aNewFeatuerLab);
+    }
+    // 2. copy attributes
+    std::set<TCollection_AsciiString> aCoordinateLabels;
+    Model_Tools::labelsOfCoordinates(aCoordinateLabels, aRelocTable);
+    TDF_ListIteratorOfLabelList aNewIt(anAllNewFeatures);
+    for (TDF_ChildIterator anIt(aTempDoc->Main()); anIt.More(); anIt.Next()) {
+      TDF_Label aCurrentLab = anIt.Value();
+      TDF_Label aFeatureLab = aNewIt.Value();
+      if (aFeatureLab.IsNull())
+        anAllNewFeatures.Remove(aNewIt);
+      else {
+        Model_Tools::copyAttrsAndKeepRefsToCoordinates(
+            aCurrentLab, aFeatureLab, aCoordinateLabels, aRelocTable);
+        aNewIt.Next();
+      }
+    }
+
+    myObjs->synchronizeFeatures(anAllNewFeatures, true, false, false, true);
+  }
+
+  if (anApp->CanClose(aTempDoc) == CDM_CCS_OK)
+    anApp->Close(aTempDoc);
+  return isOk;
+}
+
+static bool saveDocument(Handle(Model_Application) theApp,
+                         Handle(TDocStd_Document) theDoc,
+                         const TCollection_ExtendedString& theFilename)
+{
+  PCDM_StoreStatus aStatus;
+  try {
+    // create the directory to save the document
+    OSD_Path aPathToFile = UTL::Path(theFilename);
+    aPathToFile.SetName("");
+    aPathToFile.SetExtension("");
+    OSD_Directory aBaseDir(aPathToFile);
+    if (aPathToFile.TrekLength() != 0 && !aBaseDir.Exists())
+      aBaseDir.Build(OSD_Protection());
+    // save the document
+    aStatus = theApp->SaveAs(theDoc, theFilename);
+  }
+  catch (Standard_Failure const& anException) {
+    Events_InfoMessage("Model_Document",
+      "Exception in saving of document: %1").arg(anException.GetMessageString()).send();
+    return false;
+  }
+  bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj;
+  if (!isDone) {
+    switch (aStatus) {
+    case PCDM_SS_DriverFailure:
+      Events_InfoMessage("Model_Document",
+        "Can not save document: save driver-library failure").send();
+      break;
+    case PCDM_SS_WriteFailure:
+      Events_InfoMessage("Model_Document", "Can not save document: file writing failure").send();
+      break;
+    case PCDM_SS_Failure:
+    default:
+      Events_InfoMessage("Model_Document", "Can not save document").send();
+      break;
+    }
+  }
+  return isDone;
 }
 
 bool Model_Document::save(
@@ -374,34 +504,7 @@ bool Model_Document::save(
   }
   // filename in the dir is id of document inside of the given directory
   TCollection_ExtendedString aPath(DocFileName(theDirName, theFileName));
-  PCDM_StoreStatus aStatus;
-  try {
-    aStatus = anApp->SaveAs(myDoc, aPath);
-  } catch (Standard_Failure const& anException) {
-    Events_InfoMessage("Model_Document",
-        "Exception in saving of document: %1").arg(anException.GetMessageString()).send();
-    if (aWasCurrent.get()) { // return the current feature to the initial position
-      setCurrentFeature(aWasCurrent, false);
-      aSession->setCheckTransactions(true);
-    }
-    return false;
-  }
-  bool isDone = aStatus == PCDM_SS_OK || aStatus == PCDM_SS_No_Obj;
-  if (!isDone) {
-    switch (aStatus) {
-      case PCDM_SS_DriverFailure:
-        Events_InfoMessage("Model_Document",
-                           "Can not save document: save driver-library failure").send();
-        break;
-      case PCDM_SS_WriteFailure:
-        Events_InfoMessage("Model_Document", "Can not save document: file writing failure").send();
-        break;
-      case PCDM_SS_Failure:
-      default:
-        Events_InfoMessage("Model_Document", "Can not save document").send();
-        break;
-    }
-  }
+  bool isDone = saveDocument(anApp, myDoc, aPath);
 
   if (aWasCurrent.get()) { // return the current feature to the initial position
     setCurrentFeature(aWasCurrent, false);
@@ -444,6 +547,44 @@ bool Model_Document::save(
   return isDone;
 }
 
+bool Model_Document::save(const char* theFilename,
+                          const std::list<FeaturePtr>& theExportFeatures) const
+{
+  Handle(Model_Application) anApp = Model_Application::getApplication();
+  TCollection_ExtendedString aFormat;
+  if (!anApp->Format(theFilename, aFormat))
+    return false;
+
+  Handle(TDocStd_Document) aTempDoc = new TDocStd_Document(aFormat);
+  TDF_Label aMain = aTempDoc->Main();
+
+  Handle(TDF_RelocationTable) aRelocTable = new TDF_RelocationTable();
+  std::list<FeaturePtr>::const_iterator anIt = theExportFeatures.begin();
+  // Perform the copying twice for correct references:
+  // 1. copy labels hierarchy and fill the relocation table
+  for (; anIt != theExportFeatures.end(); ++anIt) {
+    TDF_Label aFeatureLab = aMain.NewChild();
+    std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>((*anIt)->data());
+    Model_Tools::copyLabels(aData->label().Father(), aFeatureLab, aRelocTable);
+  }
+  // 2. copy attributes
+  std::set<TCollection_AsciiString> aCoordinateLabels;
+  Model_Tools::labelsOfCoordinates(aCoordinateLabels, aRelocTable);
+  TDF_ChildIterator aChildIt(aMain);
+  for (anIt = theExportFeatures.begin(); anIt != theExportFeatures.end(); ++anIt) {
+    TDF_Label aFeatureLab = aChildIt.Value();
+    std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>((*anIt)->data());
+    Model_Tools::copyAttrsAndKeepRefsToCoordinates(
+        aData->label().Father(), aFeatureLab, aCoordinateLabels, aRelocTable);
+    aChildIt.Next();
+  }
+
+  bool isDone = saveDocument(anApp, aTempDoc, theFilename);
+  if (aTempDoc->CanClose() == CDM_CCS_OK)
+    aTempDoc->Close();
+  return isDone;
+}
+
 void Model_Document::close(const bool theForever)
 {
   std::shared_ptr<ModelAPI_Session> aPM = Model_Session::get();
index 72a570738b5080a167838dc2fa0830d3f43eeebf..023770a4d0864ea532abe78a84989b856fcca366 100644 (file)
@@ -56,6 +56,17 @@ class Model_Document : public ModelAPI_Document
   MODEL_EXPORT virtual bool load(
     const char* theDirName, const char* theFileName, DocumentPtr theThis);
 
+  //! Loads the OCAF document from the file into the current document.
+  //! All the features are added after the active feature.
+  //! \param theFileName name of the file to import
+  //! \param theImported list of features imported from the file
+  //! \param theCheckBefore verify the document does not contain unappropriate features
+  //!                       (useful for import to PartSet)
+  //! \returns true if file was loaded successfully
+  MODEL_EXPORT virtual bool import(const char* theFileName,
+                                   std::list<std::shared_ptr<ModelAPI_Feature> >& theImported,
+                                   bool theCheckBefore = false);
+
   //! Saves the OCAF document to the file.
   //! \param theDirName directory where the document will be saved
   //! \param theFileName a name of the document file to store
@@ -64,6 +75,12 @@ class Model_Document : public ModelAPI_Document
   MODEL_EXPORT virtual bool save(
     const char* theDirName, const char* theFileName, std::list<std::string>& theResults);
 
+  //! Export the list of features to the file
+  //! \param theFilename path to save the file
+  //! \param theExportFeatures list of features to export
+  MODEL_EXPORT virtual bool save(const char* theFilename,
+    const std::list<std::shared_ptr<ModelAPI_Feature> >& theExportFeatures) const;
+
   //! Removes document data
   //! \param theForever if it is false, document is just hidden
   //!                   (to keep possibility make it back on Undo/Redo)
diff --git a/src/Model/Model_Tools.cpp b/src/Model/Model_Tools.cpp
new file mode 100644 (file)
index 0000000..4883180
--- /dev/null
@@ -0,0 +1,181 @@
+// 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
+//
+
+#include <Model_Tools.h>
+#include <Model_Data.h>
+
+#include <ModelAPI_Document.h>
+#include <ModelAPI_Feature.h>
+#include <ModelAPI_Result.h>
+#include <ModelAPI_Session.h>
+
+#include <ConstructionPlugin_Axis.h>
+#include <ConstructionPlugin_Plane.h>
+#include <ConstructionPlugin_Point.h>
+
+#include <Standard_GUID.hxx>
+
+#include <TDataStd_Comment.hxx>
+#include <TDataStd_AsciiString.hxx>
+
+#include <TDF_AttributeIterator.hxx>
+#include <TDF_ChildIterator.hxx>
+#include <TDF_Reference.hxx>
+#include <TDF_RelocationTable.hxx>
+#include <TDF_Tool.hxx>
+
+void Model_Tools::copyLabels(TDF_Label theSource, TDF_Label theDestination,
+                             Handle(TDF_RelocationTable) theRelocTable)
+{
+  theRelocTable->SetRelocation(theSource, theDestination);
+  // copy the sub-labels hierarchy
+  TDF_ChildIterator aSubLabsIter(theSource);
+  for (; aSubLabsIter.More(); aSubLabsIter.Next()) {
+    copyLabels(aSubLabsIter.Value(),
+               theDestination.FindChild(aSubLabsIter.Value().Tag()),
+               theRelocTable);
+  }
+}
+
+void Model_Tools::copyAttrs(TDF_Label theSource, TDF_Label theDestination,
+                            Handle(TDF_RelocationTable) theRelocTable)
+{
+  TDF_AttributeIterator anAttrIter(theSource);
+  for(; anAttrIter.More(); anAttrIter.Next()) {
+    Handle(TDF_Attribute) aTargetAttr;
+    if (!theDestination.FindAttribute(anAttrIter.Value()->ID(), aTargetAttr)) {
+      // create a new attribute if not yet exists in the destination
+           aTargetAttr = anAttrIter.Value()->NewEmpty();
+      theDestination.AddAttribute(aTargetAttr);
+    }
+    // no special relocation, empty map, but self-relocation is on: copy references w/o changes
+    Handle(TDF_RelocationTable) aRelocTable =
+        theRelocTable.IsNull() ? new TDF_RelocationTable(Standard_True) : theRelocTable;
+    anAttrIter.Value()->Paste(aTargetAttr, aRelocTable);
+    // an exception: if a source reference refers itself, a copy must also refer itself
+    if (aTargetAttr->ID() == TDF_Reference::GetID()) {
+      Handle(TDF_Reference) aTargetRef = Handle(TDF_Reference)::DownCast(aTargetAttr);
+      if (aTargetRef->Get().IsEqual(anAttrIter.Value()->Label()))
+        aTargetRef->Set(aTargetRef->Label());
+    }
+  }
+  // copy the sub-labels content
+  TDF_ChildIterator aSubLabsIter(theSource);
+  for(; aSubLabsIter.More(); aSubLabsIter.Next()) {
+    copyAttrs(aSubLabsIter.Value(),
+              theDestination.FindChild(aSubLabsIter.Value().Tag()),
+              theRelocTable);
+  }
+}
+
+static TCollection_AsciiString labelToString(TDF_Label theLabel)
+{
+  TCollection_AsciiString aLabString;
+  TDF_Tool::Entry(theLabel, aLabString);
+  return aLabString;
+}
+
+static void makeExternalReference(TDF_Label theDestination, TDF_Label theReferred)
+{
+  Handle(TDF_Attribute) aReference, aComment, aString;
+  theDestination.FindAttribute(TDF_Reference::GetID(), aReference);
+  // create new attributes if not yet exists in the destination
+  if (!theDestination.FindAttribute(TDataStd_Comment::GetID(), aComment)) {
+    aComment = new TDataStd_Comment;
+    theDestination.AddAttribute(aComment);
+  }
+  if (!theDestination.FindAttribute(TDataStd_AsciiString::GetID(), aString)) {
+    aString = new TDataStd_AsciiString;
+    theDestination.AddAttribute(aString);
+  }
+  // reference to itself
+  Handle(TDF_Reference)::DownCast(aReference)->Set(theDestination, theDestination);
+  // ID of the document
+  std::ostringstream aDocIdStr;
+  aDocIdStr << ModelAPI_Session::get()->moduleDocument()->id();
+  Handle(TDataStd_Comment)::DownCast(aComment)->Set(aDocIdStr.str().c_str());
+  // value of referred label
+  Handle(TDataStd_AsciiString)::DownCast(aString)->Set(labelToString(theReferred));
+}
+
+void Model_Tools::copyAttrsAndKeepRefsToCoordinates(
+    TDF_Label theSource,
+    TDF_Label theDestination,
+    const std::set<TCollection_AsciiString>& theCoordinateLabels,
+    Handle(TDF_RelocationTable) theRelocTable)
+{
+  TDF_AttributeIterator anAttrIter(theSource);
+  for(; anAttrIter.More(); anAttrIter.Next()) {
+    Handle(TDF_Attribute) aTargetAttr;
+    if (!theDestination.FindAttribute(anAttrIter.Value()->ID(), aTargetAttr)) {
+      // create a new attribute if not yet exists in the destination
+           aTargetAttr = anAttrIter.Value()->NewEmpty();
+      theDestination.AddAttribute(aTargetAttr);
+    }
+    anAttrIter.Value()->Paste(aTargetAttr, theRelocTable);
+    if (aTargetAttr->ID() == TDF_Reference::GetID()) {
+      Handle(TDF_Reference) aTargetRef = Handle(TDF_Reference)::DownCast(aTargetAttr);
+      if (aTargetRef->Get().IsNull()) {
+        // may be refer to a cartesian coordinate entity
+        Handle(TDF_Reference) aSourceRef = Handle(TDF_Reference)::DownCast(anAttrIter.Value());
+        if (!aSourceRef.IsNull() && !aSourceRef->Get().IsNull()) {
+          std::set<TCollection_AsciiString>::const_iterator aFound =
+              theCoordinateLabels.find(labelToString(aSourceRef->Get()));
+          if (aFound != theCoordinateLabels.end())
+            makeExternalReference(theDestination, aSourceRef->Get());
+        }
+      }
+      else if (aTargetRef->Get().IsEqual(anAttrIter.Value()->Label())) {
+        // a source reference refers itself, a copy must also refer itself
+        aTargetRef->Set(aTargetRef->Label());
+      }
+    }
+  }
+  // copy the sub-labels content
+  TDF_ChildIterator aSubLabsIter(theSource);
+  for(; aSubLabsIter.More(); aSubLabsIter.Next()) {
+    copyAttrsAndKeepRefsToCoordinates(
+        aSubLabsIter.Value(), theDestination.FindChild(aSubLabsIter.Value().Tag()),
+        theCoordinateLabels, theRelocTable);
+  }
+}
+
+void Model_Tools::labelsOfCoordinates(std::set<TCollection_AsciiString>& theCoordinateLabels,
+                                      Handle(TDF_RelocationTable) theRelocTable)
+{
+  DocumentPtr aPartSet = ModelAPI_Session::get()->moduleDocument();
+  std::list<FeaturePtr> aFeatures = aPartSet->allFeatures();
+  for (std::list<FeaturePtr>::iterator aFIt = aFeatures.begin(); aFIt != aFeatures.end(); ++aFIt) {
+    FeaturePtr aCurFeat = *aFIt;
+    if (!aCurFeat->isInHistory() &&
+        (aCurFeat->getKind() == ConstructionPlugin_Point::ID() ||
+         aCurFeat->getKind() == ConstructionPlugin_Axis::ID() ||
+         aCurFeat->getKind() == ConstructionPlugin_Plane::ID())) {
+      ResultPtr aResult = aCurFeat->lastResult();
+      if (aResult) {
+        std::shared_ptr<Model_Data> aResData =
+            std::dynamic_pointer_cast<Model_Data>(aResult->data());
+        TDF_Label aLab = aResData->label().Father();
+        theCoordinateLabels.insert(labelToString(aLab));
+        // set relocation to empty, references will be set correctly while copying attributes
+        theRelocTable->SetRelocation(aLab, TDF_Label());
+      }
+    }
+  }
+}
diff --git a/src/Model/Model_Tools.h b/src/Model/Model_Tools.h
new file mode 100644 (file)
index 0000000..b7ec96c
--- /dev/null
@@ -0,0 +1,56 @@
+// 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
+//
+
+#ifndef Model_Tools_H_
+#define Model_Tools_H_
+
+#include <Model.h>
+
+#include <TDF_Label.hxx>
+#include <TDF_RelocationTable.hxx>
+
+#include <memory>
+#include <set>
+
+/// A collection of methods useful for different parts of data model.
+class Model_Tools
+{
+public:
+  /// makes copy of label and all its sub-labels without copying the attributes;
+  /// and feel the relocation table
+  static void copyLabels(TDF_Label theSource, TDF_Label theDestination,
+                         Handle(TDF_RelocationTable) theRelocTable);
+
+  /// makes copy of all attributes on the given label and all sub-labels
+  static void copyAttrs(TDF_Label theSource, TDF_Label theDestination,
+                        Handle(TDF_RelocationTable) theRelocTable = Handle(TDF_RelocationTable)());
+
+  /// makes copy of all attributes on the given label and all sub-labels,
+  /// but keep references to the Origin, coordinate axes and coordinate planes
+  static void copyAttrsAndKeepRefsToCoordinates(TDF_Label theSource, TDF_Label theDestination,
+      const std::set<TCollection_AsciiString>& theCoordinateLabels,
+      Handle(TDF_RelocationTable) theRelocTable);
+
+  /// collect labels of coordinate planes, axes, and origin
+  static void labelsOfCoordinates(
+      std::set<TCollection_AsciiString>& theCoordinateLabels,
+      Handle(TDF_RelocationTable) theRelocTable);
+};
+
+#endif
index 9cc95bad2be1874f0205d34a714ba8fb5cea26ce..6900965d5104c84795f3d96689029be5b19ad14b 100644 (file)
@@ -259,6 +259,23 @@ public:
   MODELAPI_EXPORT virtual std::shared_ptr<ModelAPI_Feature> nextFeature(
     std::shared_ptr<ModelAPI_Feature> theCurrent, const bool theReverse = false) const = 0;
 
+  /// Loads the OCAF document from the file into the current document.
+  /// All the features are added after the active feature.
+  /// \param theFileName name of the file to import
+  /// \param theImported list of features imported from the file
+  /// \param theCheckBefore verify the document does not contain unappropriate features
+  ///                       (useful for import to PartSet)
+  /// \returns true if file was loaded successfully
+  MODELAPI_EXPORT virtual bool import(const char* theFileName,
+                                      std::list<std::shared_ptr<ModelAPI_Feature> >& theImported,
+                                      bool theCheckBefore = false) = 0;
+
+  /// Export the list of features to the file
+  /// \param theFilename path to save the file
+  /// \param theExportFeatures list of features to export
+  MODELAPI_EXPORT virtual bool save(const char* theFilename,
+    const std::list<std::shared_ptr<ModelAPI_Feature> >& theExportFeatures) const = 0;
+
 protected:
   //! Only for SWIG wrapping it is here
   MODELAPI_EXPORT ModelAPI_Document();
index fc73000c04b2fb1753ed3a6e2dc68ef128244d02..2cf757443be12442821253fe138cfb1d0e9b11a4 100644 (file)
@@ -368,6 +368,8 @@ std::string ModelHighAPI_FeatureStore::dumpAttr(const AttributePtr& theAttr) {
     double aValues[3] = {anAttr->x(), anAttr->y(), anAttr->z()};
     dumpArray(aResult, aValues, 3);
   } else if (aType == GeomDataAPI_Dir::typeId()) {
+    if (theAttr->id() == "DistanceDirection")
+      return "__notcase__";
     AttributeDirPtr anAttr = std::dynamic_pointer_cast<GeomDataAPI_Dir>(theAttr);
     double aValues[3] = {anAttr->x(), anAttr->y(), anAttr->z()};
     dumpArray(aResult, aValues, 3);
index 9b2d538b2015ca93f22a65af85695f2f70893585..b2f9cc08f83f0f00de4622a48963ecc44d5857a5 100644 (file)
@@ -55,7 +55,6 @@ SET(PROJECT_HEADERS
   ModuleBase_ListView.h
   ModuleBase_ModelWidget.h
   ModuleBase_Operation.h
-  ModuleBase_OperationAction.h
   ModuleBase_OperationDescription.h
   ModuleBase_OperationFeature.h
   ModuleBase_PageBase.h
@@ -108,6 +107,7 @@ SET(PROJECT_HEADERS
   ModuleBase_WidgetSelectionFilter.h
   ModuleBase_IStepPrs.h
   ModuleBase_SelectionFilterType.h
+  ModuleBase_WidgetUndoLabel.h
 )
 
 SET(PROJECT_MOC_HEADERS
@@ -124,7 +124,6 @@ SET(PROJECT_MOC_HEADERS
   ModuleBase_ModelDialogWidget.h
   ModuleBase_ModelWidget.h
   ModuleBase_Operation.h
-  ModuleBase_OperationAction.h
   ModuleBase_OperationFeature.h
   ModuleBase_PagedContainer.h
   ModuleBase_PageGroupBox.h
@@ -158,6 +157,7 @@ SET(PROJECT_MOC_HEADERS
   ModuleBase_WidgetRadiobox.h
   ModuleBase_WidgetPointInput.h
   ModuleBase_WidgetSelectionFilter.h
+  ModuleBase_WidgetUndoLabel.h
 )
 
 SET(PROJECT_SOURCES
@@ -180,7 +180,6 @@ SET(PROJECT_SOURCES
   ModuleBase_ListView.cpp
   ModuleBase_ModelWidget.cpp
   ModuleBase_Operation.cpp
-  ModuleBase_OperationAction.cpp
   ModuleBase_OperationDescription.cpp
   ModuleBase_OperationFeature.cpp
   ModuleBase_PageBase.cpp
@@ -228,6 +227,7 @@ SET(PROJECT_SOURCES
   ModuleBase_WidgetPointInput.cpp
   ModuleBase_WidgetSelectionFilter.cpp
   ModuleBase_IStepPrs.cpp
+  ModuleBase_WidgetUndoLabel.cpp
 )
 
 SET(TEXT_RESOURCES
index 1c887faa49d28a30870458d79fff76f5d171c3bf..3404b55f282490d7571eca20368005f00d16a52f 100644 (file)
@@ -29,6 +29,9 @@
 #include <QRadioButton>
 #include <QToolButton>
 
+const QString AStyle = "QToolButton:checked {border: 1px solid black; background-color:#C0DCF3}";
+
+
 ModuleBase_ChoiceCtrl::ModuleBase_ChoiceCtrl(QWidget* theParent,
                                              const QStringList& theChoiceList,
                                              const QStringList& theIconsList,
@@ -68,6 +71,7 @@ ModuleBase_ChoiceCtrl::ModuleBase_ChoiceCtrl(QWidget* theParent,
           QPixmap aIcon = ModuleBase_IconFactory::loadPixmap(theIconsList.at(aId));
           aBtn->setIcon(aIcon);
           aBtn->setIconSize(aIcon.size());
+          aBtn->setStyleSheet(AStyle);
 
           aBtnLayout->addWidget(aBtn);
           myButtons->addButton(aBtn, aId++);
index a2c250dbf1d380f00668db7510b3f3390ac8c074..963070788e390a26d96e8c9f394b29aa97597dc2 100644 (file)
@@ -169,10 +169,11 @@ class MODULEBASE_EXPORT ModuleBase_IModule : public QObject
 
   /// Have an opportunity to create widgets for the current operation
   /// instead of standard creation in workshop
-  /// \param theOperation a started operation
+  /// \param theFeature a feature of the started operation
+  /// \param theXmlRepr an XML representation of the operation
   /// \param theWidgets a list of created widgets
   /// \return boolean result, false by default
-  virtual bool createWidgets(ModuleBase_Operation* theOperation,
+  virtual bool createWidgets(const FeaturePtr& theFeature, const QString& theXmlRepr,
                              QList<ModuleBase_ModelWidget*>& theWidgets) const { return false; }
 
   //! Returns True if there are available Undos and there is not an active operation
index df66658a7e0b9f5c5a0eafbeb45e87acaf630745..cd8b25c401d5ac5bf1537b5e3b4aed2fe1632f59 100644 (file)
@@ -78,3 +78,14 @@ ModuleBase_ModelWidget* ModuleBase_IPropertyPanel::findFirstAcceptingValueWidget
   }
   return aFirstWidget;
 }
+
+bool ModuleBase_IPropertyPanel::isModified() const
+{
+  bool isModified = false;
+  QList<ModuleBase_ModelWidget*> aWidgets = modelWidgets();
+  foreach(ModuleBase_ModelWidget* aWgt, aWidgets) {
+    bool aRes = aWgt->isModified();
+    isModified |= aRes;
+  }
+  return isModified;
+}
index a4f954ea03ad8688b67556100c6d32cce1cff50c..16fdecf13b7a38ca9054e2e1e0c986bbf6f28346 100644 (file)
@@ -88,6 +88,9 @@ public:
   /// The method is called on accepting of operation
   virtual void onAcceptData() = 0;
 
+  /// Returns True if data of its feature was modified during operation
+  virtual bool isModified() const;
+
   /// Returns the first widget, where canAcceptFocus returns true
   /// \return a widget or null
   static ModuleBase_ModelWidget* findFirstAcceptingValueWidget(
index 56ca626301444da926d7014608a8bb3b5c1d50f6..466b06641d828f285841f64c3e189555e2c5d9cd 100644 (file)
@@ -152,6 +152,15 @@ Q_OBJECT
   //! \param theAIS the object which has to be activated
   virtual void applyCurrentSelectionModes(const AISObjectPtr& theAIS) = 0;
 
+  //! Undo last command
+  virtual void undo() = 0;
+
+  //! Set enabled state of cancel button in property panel
+  virtual void setCancelEnabled(bool toEnable) = 0;
+
+  //! Returns current state of cancel button
+  virtual bool isCancelEnabled() const = 0;
+
 signals:
   /// Signal selection changed.
   void selectionChanged();
index d9fe13d2c18339a17daaafebe041484adacb8e43..5fd8909eefb4374cf7f6a24549fe17f26668cc24 100644 (file)
@@ -77,6 +77,19 @@ void ModuleBase_ListView::getSelectedIndices(std::set<int>& theIndices)
   }
 }
 
+//********************************************************************
+void  ModuleBase_ListView::selectIndices(const std::set<int>& theIndices)
+{
+  myListControl->clearSelection();
+  for (int i = 0; i < myListControl->count(); i++) {
+    QListWidgetItem* anItem = myListControl->item(i);
+    int aId = anItem->data(ATTRIBUTE_SELECTION_INDEX_ROLE).toInt();
+    if (theIndices.find(aId) != theIndices.end()) {
+      anItem->setSelected(true);
+    }
+  }
+}
+
 //********************************************************************
 void ModuleBase_ListView::removeSelectedItems()
 {
index aa973845e1c4ce44c5058e03ed309a308182eead..aa0620e9045471dbac2eb42f2e0d66d69f2daded 100644 (file)
@@ -118,6 +118,10 @@ public:
   /// \param theIndices an output container for indices
   void getSelectedIndices(std::set<int>& theIndices);
 
+  /// Selects items with indices
+  /// \param theIndices indices
+  void selectIndices(const std::set<int>& theIndices);
+
   /// Removes selected items from the list widget
   void removeSelectedItems();
 
index a563c277981ff7cf1ca87f8a0174cd535209eac2..de9582a5f86b32eb7c960bfb775d0ba447c2aa7c 100644 (file)
@@ -425,6 +425,12 @@ bool ModuleBase_ModelWidget::blockValueState(const bool theBlocked)
 //**************************************************************
 bool ModuleBase_ModelWidget::restoreValue()
 {
+  if (!isEnabled()) {
+    // This code works in inspection panel
+    ModelAPI_ValidatorsFactory* aValidators = ModelAPI_Session::get()->validators();
+    if (!aValidators->isCase(myFeature, attributeID()))
+      return false; // if it is not an active case for the widget
+  }
   emit beforeValuesRestored();
   bool isDone = restoreValueCustom();
   emit afterValuesRestored();
index 4fb4ad55249d3381aa2c59a7acce5d79f9e6c9e3..268dcaf229ba3844251834f4fd3efbc7646600a3 100644 (file)
@@ -303,6 +303,16 @@ Q_OBJECT
   /// By default this slot does nothing
   virtual void onFeatureAccepted() {}
 
+  /// Returns True in case if the widget contains useful information for inspection tool
+  virtual bool isInformative() const { return true; }
+
+  /// If widgets has several panels then this method has to show a page which contains information
+  /// for current feature. By default does nothing
+  virtual void showInformativePage() {}
+
+  /// Returns True if data of its attribute was modified
+  virtual bool isModified() const { return false; }
+
 signals:
   /// The signal about widget values are to be changed
   void beforeValuesChanged();
index b4f4a38b05296e9a20a94d30cabc41198ee4a188..81da1e47d898882b53af79b8c3b81823a346074e 100644 (file)
@@ -40,6 +40,8 @@
 #include <GeomAPI_Pnt2d.h>
 
 #include <Events_Loop.h>
+#include <Config_WidgetAPI.h>
+#include <Config_Keywords.h>
 
 #include <QTimer>
 
@@ -50,7 +52,8 @@
 ModuleBase_Operation::ModuleBase_Operation(const QString& theId, QObject* theParent)
     : QObject(theParent),
       myIsModified(false),
-      myPropertyPanel(NULL)
+      myPropertyPanel(NULL),
+  myHideFacesVisibilityState(false)
 {
   myDescription = new ModuleBase_OperationDescription(theId);
 }
@@ -176,3 +179,17 @@ bool ModuleBase_Operation::isGranted(QString theId) const
 {
   return myGrantedIds.contains(theId);
 }
+
+bool  ModuleBase_Operation::isModified() const
+{
+  if (myDescription->hasXmlRepresentation()) {
+    Config_WidgetAPI aWidgetApi(myDescription->xmlRepresentation().toStdString());
+    if (!aWidgetApi.getBooleanAttribute(ABORT_CONFIRMATION, true))
+      return false;
+  }
+  //if (myPropertyPanel)
+  //  return myPropertyPanel->isModified();
+  //return false;
+  // Most of operation causes creation of a feature
+  return true;
+}
index 845f6368608e555ef9890aa7b0fcba0ed7fda172..6ce75a83968fd4db49f5d5756f891aa7f9198dae 100644 (file)
@@ -80,10 +80,7 @@ Q_OBJECT
   virtual bool isGranted(QString theId) const;
 
   /// Returns True if data of its feature was modified during operation
-  virtual bool isModified() const { return myIsModified; }
-
-  /// Change the modified state of the operation
-  void setIsModified(const bool theIsModified) { myIsModified = theIsModified;  }
+  virtual bool isModified() const;
 
   /// Returns operations Id from it's description
   QString id() const;
@@ -105,6 +102,10 @@ Q_OBJECT
     myHelpFileName = theName;
   }
 
+  void setHideFacesVisible(bool isVisible) { myHideFacesVisibilityState = isVisible; }
+
+  bool isHideFacesVisible() const { return myHideFacesVisibilityState; }
+
 signals:
   /// The operation is started
   void beforeStarted();
@@ -209,6 +210,9 @@ private:
   ModuleBase_IPropertyPanel* myPropertyPanel;
 
   QString myHelpFileName;
+
+  /// Visibility state of HideFaces panel before the operation launch
+  bool myHideFacesVisibilityState;
 };
 
 #endif
diff --git a/src/ModuleBase/ModuleBase_OperationAction.cpp b/src/ModuleBase/ModuleBase_OperationAction.cpp
deleted file mode 100644 (file)
index c55c693..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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
-//
-
-#include "ModuleBase_OperationAction.h"
-
-ModuleBase_OperationAction::ModuleBase_OperationAction(const QString& theId, QObject* theParent)
- : ModuleBase_Operation(theId, theParent)
-{
-}
-
-ModuleBase_OperationAction::~ModuleBase_OperationAction()
-{
-}
-
-bool ModuleBase_OperationAction::commit()
-{
-  // the action is supposed to perform a single modification,
-  // so the operation returns modified state
-  setIsModified(true);
-
-  return ModuleBase_Operation::commit();
-}
diff --git a/src/ModuleBase/ModuleBase_OperationAction.h b/src/ModuleBase/ModuleBase_OperationAction.h
deleted file mode 100644 (file)
index 35b9acf..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-// 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
-//
-
-#ifndef ModuleBase_OperationAction_H
-#define ModuleBase_OperationAction_H
-
-#include <ModuleBase.h>
-
-#include <ModuleBase_Operation.h>
-
-/*!
- * \class ModuleBase_OperationAction
- * \ingroup GUI
- * \brief Base class for action operations
- *
- *  This is an action-like operation, which modifies the structure of data through
- *  starting/comitting transactions in the document. This operations are single stepped,
- *  and have no filled property panel, like Delete/Detach.
- */
-
-class MODULEBASE_EXPORT ModuleBase_OperationAction : public ModuleBase_Operation
-{
-Q_OBJECT
-
- public:
-  /// Constructor
-  /// \param theId the operation identifier
-  /// \param theParent the QObject parent
-  ModuleBase_OperationAction(const QString& theId = "", QObject* theParent = 0);
-  /// Destructor
-  virtual ~ModuleBase_OperationAction();
-
- public slots:
-  /// Commits the operation. Change is modified state to true value.
-  /// \return the result of commit
-  virtual bool commit();
-};
-
-#endif
index aa2d464fcb4fda8c66cf1f089b413a279ec4cdc2..98cbc74403f48ac321af785e2d41a49786e1d947 100644 (file)
@@ -175,22 +175,6 @@ FeaturePtr ModuleBase_OperationFeature::createFeature(const bool theFlushMessage
     std::shared_ptr<ModelAPI_Document> aDoc = ModelAPI_Session::get()->activeDocument();
     myFeature = aDoc->addFeature(getDescription()->operationId().toStdString());
   }
-  if (myFeature) {  // TODO: generate an error if feature was not created
-    setIsModified(true);
-    // Model update should call "execute" of a feature.
-    //myFeature->execute();
-    // Init default values
-    /*QList<ModuleBase_ModelWidget*> aWidgets = getDescription()->modelWidgets();
-     QList<ModuleBase_ModelWidget*>::const_iterator anIt = aWidgets.begin(), aLast = aWidgets.end();
-     for (; anIt != aLast; anIt++) {
-     (*anIt)->storeValue(aFeature);
-     }*/
-  }
-
-  //if (theFlushMessage) {
-  //  Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_CREATED));
-  //  Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
-  //}
   return myFeature;
 }
 
@@ -249,7 +233,6 @@ bool ModuleBase_OperationFeature::start()
 #ifdef DEBUG_OPERATION_START
   qDebug("ModuleBase_OperationFeature::start -- begin");
 #endif
-  setIsModified(false);
   QString anId = getDescription()->operationId();
   if (myIsEditing) {
     anId = anId.append(EditSuffix());
index e2d35c87b2a8111facc8ebd31d15db737b151aee..9bf101d3ffffb9612aa9310509d56485afcf38c0 100644 (file)
@@ -145,7 +145,7 @@ void ModuleBase_ResultPrs::setEdgesDefaultColor()
 
 
 //********************************************************************
-bool ModuleBase_ResultPrs::setSubShapeHidden(const NCollection_List<TopoDS_Shape>& theShapes)
+void ModuleBase_ResultPrs::setSubShapeHidden(const TopoDS_ListOfShape& theShapes)
 {
   bool isModified = false;
 
@@ -153,48 +153,16 @@ bool ModuleBase_ResultPrs::setSubShapeHidden(const NCollection_List<TopoDS_Shape
   BRep_Builder aBBuilder;
   aBBuilder.MakeCompound (aCompound);
 
-  TopoDS_Compound aShownComp;
-  if (myIsSubstituted)
-    aShownComp = TopoDS::Compound(Shape());
+  myHiddenSubShapes = theShapes;
+  collectSubShapes(aBBuilder, aCompound, myOriginalShape, myHiddenSubShapes);
+  myVisibleCompound = aCompound;
 
-  // restore hidden shapes if there are not the shapes in parameter container
-  NCollection_List<TopoDS_Shape> aVisibleSubShapes;
-  NCollection_List<TopoDS_Shape>::Iterator aHiddenIt(myHiddenSubShapes);
-  for (; aHiddenIt.More(); aHiddenIt.Next()) {
-    if (!theShapes.Contains(aHiddenIt.Value())) {
-      aVisibleSubShapes.Append(aHiddenIt.Value());
-    }
-    else {
-      aBBuilder.Add(aCompound, aHiddenIt.Value());
-      if (!aShownComp.IsNull())
-        aBBuilder.Remove(aShownComp, aHiddenIt.Value());
-    }
-  }
-  isModified = !aVisibleSubShapes.IsEmpty();
-  NCollection_List<TopoDS_Shape>::Iterator aVisibleIt(aVisibleSubShapes);
-  for (; aVisibleIt.More(); aVisibleIt.Next()) {
-    if (myHiddenSubShapes.Contains(aVisibleIt.Value())) {
-      myHiddenSubShapes.Remove(aVisibleIt.Value());
-      if (!aShownComp.IsNull())
-        aBBuilder.Add(aShownComp, aVisibleIt.Value());
-    }
-  }
-  // append hidden shapes into internal container if there are not these shapes
-  NCollection_List<TopoDS_Shape>::Iterator aShapeIt(theShapes);
-  for (; aShapeIt.More(); aShapeIt.Next()) {
-    if (aShapeIt.Value().ShapeType() != TopAbs_FACE) // only face shape can be hidden
-      continue;
-
-    if (!myHiddenSubShapes.Contains(aShapeIt.Value())) {
-      myHiddenSubShapes.Append(aShapeIt.Value());
-      aBBuilder.Add (aCompound, aShapeIt.Value());
-      if (!aShownComp.IsNull())
-        aBBuilder.Remove(aShownComp, aShapeIt.Value());
-      isModified = true;
-    }
+  aBBuilder.MakeCompound (aCompound);
+  TopoDS_ListOfShape::Iterator aIt(myHiddenSubShapes);
+  for (; aIt.More(); aIt.Next()) {
+    aBBuilder.Add(aCompound, aIt.Value());
   }
   myHiddenCompound = aCompound;
-  return isModified;
 }
 
 //********************************************************************
@@ -204,9 +172,8 @@ bool ModuleBase_ResultPrs::isSubShapeHidden(const TopoDS_Shape& theShape)
     return false;
 
   // orientation of parameter shape(come from selection) may be wrong, check isEqual() to be sure
-  for (NCollection_List<TopoDS_Shape>::Iterator aShapeIt(myHiddenSubShapes); aShapeIt.More();
-    aShapeIt.Next())
-  {
+  TopoDS_ListOfShape::Iterator aShapeIt(myHiddenSubShapes);
+  for (; aShapeIt.More(); aShapeIt.Next()) {
     if (theShape.IsSame(aShapeIt.Value()))
       return true;
   }
@@ -216,14 +183,15 @@ bool ModuleBase_ResultPrs::isSubShapeHidden(const TopoDS_Shape& theShape)
 
 //********************************************************************
 bool ModuleBase_ResultPrs::hasSubShapeVisible(
-  const NCollection_List<TopoDS_Shape>& theShapesToSkip)
+  const TopoDS_ListOfShape& theShapesToSkip)
 {
   TopoDS_Compound aCompound;
   BRep_Builder aBuilder;
   aBuilder.MakeCompound (aCompound);
-  NCollection_List<TopoDS_Shape> aShapesToSkip;
-  aShapesToSkip.Append(myHiddenSubShapes);
-  for (NCollection_List<TopoDS_Shape>::Iterator anIt(theShapesToSkip); anIt.More(); anIt.Next())
+  TopoDS_ListOfShape aShapesToSkip;
+  TopoDS_ListOfShape aHiddenCopy(myHiddenSubShapes);
+  aShapesToSkip.Append(aHiddenCopy);
+  for (TopoDS_ListOfShape::Iterator anIt(theShapesToSkip); anIt.More(); anIt.Next())
     aShapesToSkip.Append(anIt.Value());
 
   collectSubShapes(aBuilder, aCompound, myOriginalShape, aShapesToSkip);
@@ -258,17 +226,8 @@ void ModuleBase_ResultPrs::Compute(
       }
     }
     else { // convert shape into SHELL
-      TopoDS_Compound aCompound;
-      if (!myIsSubstituted) {
-        BRep_Builder aBuilder;
-        aBuilder.MakeCompound(aCompound);
-        collectSubShapes(aBuilder, aCompound, myOriginalShape, myHiddenSubShapes);
-      }
-      else {
-        aCompound = TopoDS::Compound(Shape());
-      }
-      bool isEmptyShape = BOPTools_AlgoTools3D::IsEmptyShape(aCompound);
-      Set(aCompound);
+      bool isEmptyShape = BOPTools_AlgoTools3D::IsEmptyShape(myVisibleCompound);
+      Set(myVisibleCompound);
       myIsSubstituted = true;
       if (isEmptyShape)
         aReadyToDisplay = false;
@@ -301,7 +260,7 @@ void ModuleBase_ResultPrs::Compute(
 //********************************************************************
 void ModuleBase_ResultPrs::collectSubShapes(BRep_Builder& theBuilder,
   TopoDS_Shape& theCompound, const TopoDS_Shape& theShape,
-  const NCollection_List<TopoDS_Shape>& theHiddenSubShapes)
+  const TopoDS_ListOfShape& theHiddenSubShapes)
 {
   switch (theShape.ShapeType()) {
     case TopAbs_COMPSOLID:
index 1868ad8dbb4c2ada4fe36872510f02f161f053f5..84500eba65dfff600d2f6cbad61b76defbefa624 100644 (file)
@@ -91,17 +91,21 @@ public:
   /// Visual state of the face is controlled by the second parameter
   /// \param theShapes a container of shape objects
   /// \returns true if the presentation is changed, or false (if for example it was hidden)
-  Standard_EXPORT bool setSubShapeHidden(const NCollection_List<TopoDS_Shape>& theShapes);
+  Standard_EXPORT void setSubShapeHidden(const TopoDS_ListOfShape& theShapes);
 
   /// Returns true if parameter shape has been hidden
   /// \param theShape sub-shape of the presentation shape
   /// \return boolean value
   Standard_EXPORT bool isSubShapeHidden(const TopoDS_Shape& theShape);
 
+  /// Returns hidden sub shapes list
+  Standard_EXPORT const TopoDS_ListOfShape& hiddenSubShapes() const
+  { return myHiddenSubShapes; }
+
   /// Returns true if there are no hidden sub shapes or original shape has at least one not hidden
   /// \param theShapesToSkip container of shape to be hidden in the presentation (faces)
   /// \return boolean value
-  Standard_EXPORT bool hasSubShapeVisible(const NCollection_List<TopoDS_Shape>& theShapesToSkip);
+  Standard_EXPORT bool hasSubShapeVisible(const TopoDS_ListOfShape& theShapesToSkip);
 
   /// Set transparency of hidden sub shapes: if value is 1, shapes are entirely hidden
   /// \param theTransparency transparency value
@@ -144,7 +148,7 @@ private:
   /// \param theShape the processed shape
   /// \param theHiddenSubShapes container of shapes to be skipped (faces)
   void collectSubShapes(BRep_Builder& theBuilder, TopoDS_Shape& theCompound,
-    const TopoDS_Shape& theShape, const NCollection_List<TopoDS_Shape>& theHiddenSubShapes);
+    const TopoDS_Shape& theShape, const TopoDS_ListOfShape& theHiddenSubShapes);
 
   void setEdgesDefaultColor();
 
@@ -158,7 +162,8 @@ private:
   bool myIsSubstituted;
 
   /// Container of original Shape sub shape to be hidden and not selectable
-  NCollection_List<TopoDS_Shape> myHiddenSubShapes;
+  TopoDS_ListOfShape myHiddenSubShapes;
+  TopoDS_Compound myVisibleCompound; /// compound of hidden sub shapes
   TopoDS_Compound myHiddenCompound; /// compound of hidden sub shapes
   double myTransparency; ///< transparency of hidden shapes, where 0 - there is no transparency
   Handle(AIS_ColoredDrawer) myHiddenSubShapesDrawer; ///< drawer for hidden sub shapes
index 8618e3b3488e2487506a0938af04b54bc3338fd8..ab6296671ade2a08b59f768d71b71c3bf510a9e1 100644 (file)
@@ -28,6 +28,9 @@
 
 #include <ModuleBase_PagedContainer.h>
 
+const QString AStyle = "QToolButton:checked {border: 1px solid black; background-color:#C0DCF3}";
+
+
 ModuleBase_ToolBox::ModuleBase_ToolBox(QWidget* theParent, const bool theUseFrameStyleBox)
 : QFrame(theParent)
 {
@@ -71,6 +74,7 @@ void ModuleBase_ToolBox::addItem(QWidget* thePage, const QString& theName, const
   QToolButton* aButton = new QToolButton(myButtonsFrame);
   aButton->setFocusPolicy(Qt::StrongFocus);
   aButton->setCheckable(true);
+  aButton->setStyleSheet(AStyle);
   if (theIcon.isNull())
     aButton->setText(theName);
   else {
index a96e15ff07179c97dffa4719e96d4fd52423d30e..bae232f486e72a37e9ea4b87b7f53d1e63f12935 100644 (file)
@@ -257,7 +257,7 @@ void setSpinValue(QDoubleSpinBox* theSpin, double theValue)
 
 void setSpinValue(ModuleBase_ParamSpinBox* theSpin, double theValue)
 {
-  if (fabs(theSpin->value() - theValue) < tolerance)
+  if (!theSpin->text().isEmpty() && fabs(theSpin->value() - theValue) < tolerance)
     return;
   bool isBlocked = theSpin->blockSignals(true);
   theSpin->setValue(theValue);
index 4b0e41daa5be7e11d1c94dbcd10b85f36eee2b1b..c07a5f4b1d3cae63aeb73ab3a83149f2f7e8b95c 100644 (file)
@@ -195,18 +195,6 @@ bool ModuleBase_WidgetDoubleValue::restoreValueCustom()
   std::string aTextRepr = aRef->text();
   if (!aTextRepr.empty()) {
     QString aText = QString::fromStdString(aTextRepr);
-    //if (aText.endsWith('=')) {
-    //  if (!myParameter.get()) {
-    //    QString aName = aText.left(aText.indexOf('=')).trimmed();
-    //    myParameter = ModuleBase_Tools::findParameter(aName);
-    //  }
-    //  /// If myParameter is empty then it was not created because of an error
-    //  if (!myParameter.get())
-    //    return false;
-
-    //  AttributeStringPtr aExprAttr = myParameter->string("expression");
-    //  aText += aExprAttr->value().c_str();
-    //}
     ModuleBase_Tools::setSpinText(mySpinBox, aText);
   }
   else {
@@ -242,3 +230,18 @@ bool ModuleBase_WidgetDoubleValue::processEnter()
   }
   return isModified;
 }
+
+bool ModuleBase_WidgetDoubleValue::isModified() const
+{
+  QString aText = mySpinBox->text();
+  if (aText.isEmpty())
+    return false;
+
+  if (myHasDefault) {
+    bool aOk = false;
+    double aVal = aText.toDouble(&aOk);
+    if (!aOk || aVal == myDefaultVal)
+      return false;
+  }
+  return true;
+}
\ No newline at end of file
index 4854ffe7a89934b68c714eb7b2f5c9bbacd3eb71..db658ac8b2f2a2fc577335e80f0331b7d5c47bb5 100644 (file)
@@ -58,6 +58,9 @@ Q_OBJECT
   /// \return a control list
   virtual QList<QWidget*> getControls() const;
 
+  /// Returns True if data of its feature was modified during operation
+  virtual bool isModified() const;
+
  public slots:
  // Delayed value chnged: when user starts typing something,
  // it gives him a 0,5 second to finish typing, when sends valueChnaged() signal
index 0cc45bf0947f676388cd9fb748f86c0286a6169e..7f51a61a7cebe597e8b0cb4c1b41f1636c814238 100644 (file)
@@ -40,6 +40,7 @@
 #include <ModuleBase_WidgetMultiSelector.h>
 #include <ModuleBase_WidgetConcealedObjects.h>
 #include <ModuleBase_WidgetLabel.h>
+#include <ModuleBase_WidgetUndoLabel.h>
 #include <ModuleBase_WidgetToolbox.h>
 #include <ModuleBase_WidgetRadiobox.h>
 #include <ModuleBase_PageBase.h>
@@ -303,6 +304,8 @@ ModuleBase_ModelWidget* ModuleBase_WidgetFactory::createWidgetByType(const std::
     result = new ModuleBase_WidgetLabel(theParent, myWidgetApi);
   } else if (theType == WDG_DOUBLEVALUE) {
     result = new ModuleBase_WidgetDoubleValue(theParent, myWidgetApi);
+  } else if (theType == WDG_UNDOLABEL) {
+    result = new ModuleBase_WidgetUndoLabel(theParent, myWorkshop, myWidgetApi);
   } else if (theType == WDG_DOUBLEVALUELABEL) {
     result = new ModuleBase_WidgetLabelValue(theParent, myWidgetApi);
   } else if (theType == WDG_INTEGERVALUE) {
index 0e131bb1dc8dda8766947714177e795ea9a01e38..25bee53c85eab9488cf97a00f375db621d342d29 100644 (file)
@@ -230,3 +230,18 @@ bool ModuleBase_WidgetIntValue::processEnter()
   }
   return isModified;
 }
+
+bool ModuleBase_WidgetIntValue::isModified() const
+{
+  QString aText = mySpinBox->text();
+  if (aText.isEmpty())
+    return false;
+
+  if (myHasDefault) {
+    bool aOk = false;
+    int aVal = aText.toInt(&aOk);
+    if (!aOk || aVal == myDefVal)
+      return false;
+  }
+  return true;
+}
\ No newline at end of file
index 7fb7d13f487663b4c6f369ca6c3bd024e4ff33d6..955e3aa973fd7fabc19bf3b0e5569783e0e3fdef 100644 (file)
@@ -58,6 +58,9 @@ Q_OBJECT
   /// \return a control list
   virtual QList<QWidget*> getControls() const;
 
+  /// Returns True if data of its feature was modified during operation
+  virtual bool isModified() const;
+
 protected:
   /// Returns true if the event is processed.
   virtual bool processEnter();
index b717bf1039c7c0dadc958836b56836424e9db179..135d006a3805539ed3403468de543f91c4ddd9b6 100644 (file)
@@ -37,6 +37,8 @@ ModuleBase_WidgetLabel::ModuleBase_WidgetLabel(QWidget* theParent,
 : ModuleBase_ModelWidget(theParent, theData)
 {
   QString aText = translate(theData->getProperty("title"));
+  bool aIsHtml = theData->getBooleanAttribute(ATTR_HTML_STYLE, false);
+
   QString aLabelIcon = QString::fromStdString(theData->getProperty("icon"));
   myLabel = new QLabel(aText, theParent);
   if (!aLabelIcon.isEmpty()) {
@@ -48,13 +50,15 @@ ModuleBase_WidgetLabel::ModuleBase_WidgetLabel(QWidget* theParent,
   myLabel->setWordWrap(true);
   myLabel->setIndent(5);
   myLabel->setContentsMargins(0,0,0,4);
+  if (aIsHtml)
+    myLabel->setTextFormat(Qt::RichText);
 
   QVBoxLayout* aLayout = new QVBoxLayout(this);
   ModuleBase_Tools::zeroMargins(aLayout);
   aLayout->addWidget(myLabel);
   setLayout(aLayout);
 
-  std::string aStyleSheet = theData->getProperty(ATTR_STYLE_SHEET).c_str();
+  std::string aStyleSheet = theData->getProperty(ATTR_STYLE_SHEET);
   if (!aStyleSheet.empty())
     myLabel->setStyleSheet(QString("QLabel {%1}").arg(aStyleSheet.c_str()));
 }
index e915477ed7948f52023ec495ecf4cefc000e2f11..f5429cc3f24228d888ce7c8a5dd15178b2295f12 100644 (file)
@@ -160,3 +160,8 @@ bool ModuleBase_WidgetLineEdit::processEnter()
   }
   return isModified;
 }
+
+bool ModuleBase_WidgetLineEdit::isModified() const
+{
+  return !myLineEdit->text().isEmpty();
+}
\ No newline at end of file
index 05d6b1ed577d76469d61f67f42e0d4b29e27776c..c1622c4aa53ddbdadbfc900aef0755541d53ec10 100644 (file)
@@ -51,6 +51,9 @@ class MODULEBASE_EXPORT ModuleBase_WidgetLineEdit : public ModuleBase_ModelWidge
   /// Redefinition of virtual method
   virtual QList<QWidget*> getControls() const;
 
+  /// Returns True if data of its feature was modified during operation
+  virtual bool isModified() const;
+
 protected:
   /// Returns true if the event is processed.
   virtual bool processEnter();
index 0d04aeeb05866ffe459b3236b464e9a3fa84980b..2d725744873deb0a3d6cd8e6b56c66690868fdf7 100644 (file)
@@ -1180,3 +1180,8 @@ void ModuleBase_WidgetMultiSelector::onShowOnly(bool theChecked)
   } else
     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
 }
+
+bool ModuleBase_WidgetMultiSelector::isModified() const
+{
+  return myListView->getControl()->count() > 0;
+}
\ No newline at end of file
index f34e18ef568e40ffacf0ad776ebf6a952e3d7111..08aa13a39934db9b5cfa04a447d812df8de86790 100644 (file)
@@ -115,6 +115,9 @@ class MODULEBASE_EXPORT ModuleBase_WidgetMultiSelector : public ModuleBase_Widge
   /// The slot is called when user press Ok or OkPlus buttons in the parent property panel
   virtual void onFeatureAccepted();
 
+  /// Returns True if data of its feature was modified during operation
+  virtual bool isModified() const;
+
 public slots:
   /// Slot is called on selection type changed
   void onSelectionTypeChanged();
index c8d37d5d3ad3678481d40d680f07996d394746e6..127c7621e4f846c5ca09a4a4b6ff784c702d133e 100644 (file)
@@ -238,3 +238,9 @@ void ModuleBase_WidgetShapeSelector::updateSelectionName()
     }
   }
 }
+
+//********************************************************************
+bool ModuleBase_WidgetShapeSelector::isModified() const
+{
+  return !myTextLine->text().isEmpty();
+}
\ No newline at end of file
index 61b51810d7195af48e38ede379e287e989658e27..916259660ec8d89f567c95dcc3cc55d064363cbc 100644 (file)
@@ -94,6 +94,9 @@ Q_OBJECT
   /// \return a control list
   virtual QList<QWidget*> getControls() const;
 
+  /// Returns True if data of its feature was modified during operation
+  virtual bool isModified() const;
+
  protected:
   /// Saves the internal parameters to the given feature
   /// \return True in success
diff --git a/src/ModuleBase/ModuleBase_WidgetUndoLabel.cpp b/src/ModuleBase/ModuleBase_WidgetUndoLabel.cpp
new file mode 100644 (file)
index 0000000..3f5a35b
--- /dev/null
@@ -0,0 +1,54 @@
+// 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
+//
+
+#include "ModuleBase_WidgetUndoLabel.h"
+#include "ModuleBase_IWorkshop.h"
+
+#include <QPushButton>
+#include <QLayout>
+#include <QString>
+#include <QLabel>
+
+ModuleBase_WidgetUndoLabel::ModuleBase_WidgetUndoLabel(QWidget* theParent,
+  ModuleBase_IWorkshop* theWorkshop,
+  const Config_WidgetAPI* theData)
+  : ModuleBase_WidgetLabel(theParent, theData),
+  myWorkshop(theWorkshop)
+{
+  myUndoBtn = new QPushButton(tr("Undo"), this);
+  myUndoBtn->hide();
+  layout()->addWidget(myUndoBtn);
+  connect(myUndoBtn, SIGNAL(clicked(bool)), SLOT(onUndo()));
+}
+
+
+bool ModuleBase_WidgetUndoLabel::restoreValueCustom()
+{
+  bool aRes = ModuleBase_WidgetLabel::restoreValueCustom();
+  bool aError = myLabel->text().length() > 0;
+  myUndoBtn->setVisible(aError);
+  myWorkshop->setCancelEnabled(!aError);
+  return aRes;
+}
+
+
+void ModuleBase_WidgetUndoLabel::onUndo()
+{
+  myWorkshop->undo();
+}
\ No newline at end of file
diff --git a/src/ModuleBase/ModuleBase_WidgetUndoLabel.h b/src/ModuleBase/ModuleBase_WidgetUndoLabel.h
new file mode 100644 (file)
index 0000000..efa46ee
--- /dev/null
@@ -0,0 +1,55 @@
+// 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
+//
+
+#ifndef ModuleBase_WidgetUndoLabel_H
+#define ModuleBase_WidgetUndoLabel_H
+
+#include "ModuleBase.h"
+#include "ModuleBase_WidgetLabel.h"
+
+class QPushButton;
+class ModuleBase_IWorkshop;
+
+/**
+* \ingroup GUI
+* Implementation of model widget for a label control
+*/
+class MODULEBASE_EXPORT ModuleBase_WidgetUndoLabel : public ModuleBase_WidgetLabel
+{
+  Q_OBJECT
+public:
+  /// Constructor
+  /// \param theParent the parent object
+  /// \param theData the widget configuation. The attribute of the model widget is obtained from
+  ModuleBase_WidgetUndoLabel(QWidget* theParent, ModuleBase_IWorkshop* theWorkshop,
+    const Config_WidgetAPI* theData);
+
+  virtual ~ModuleBase_WidgetUndoLabel() {}
+
+  virtual bool restoreValueCustom();
+
+private slots:
+  void onUndo();
+
+private:
+  ModuleBase_IWorkshop* myWorkshop;
+  QPushButton* myUndoBtn;
+};
+
+#endif
\ No newline at end of file
index 9078329662e93044b3d61496b2e572539f63be1d..9938df1e66b911170eae3108086274f1a72ea186 100644 (file)
@@ -39,6 +39,7 @@
 #include <ModuleBase_Tools.h>
 
 #include <Events_Loop.h>
+#include <Config_PropManager.h>
 
 #include <QLayout>
 #include <QPushButton>
@@ -50,6 +51,7 @@
 #include <QEvent>
 #include <QKeyEvent>
 #include <QDialogButtonBox>
+#include <QShortcut>
 
 enum ColumnType {
   Col_Name,
@@ -238,6 +240,13 @@ ParametersPlugin_WidgetParamsMgr::ParametersPlugin_WidgetParamsMgr(QWidget* theP
   connect(myAddBtn, SIGNAL(clicked(bool)), SLOT(onAdd()));
   aBtnLayout->addWidget(myAddBtn);
 
+  QString aAddStr(Config_PropManager::string("Shortcuts", "add_parameter_shortcut").c_str());
+  if (aAddStr.isEmpty())
+    aAddStr = "Ctrl+A";
+
+  QShortcut* aAddShc = new QShortcut(QKeySequence(aAddStr), myAddBtn);
+  connect(aAddShc, SIGNAL(activated()), SLOT(onAdd()));
+
   myInsertBtn = new QPushButton(translate("Insert"), this);
   connect(myInsertBtn, SIGNAL(clicked(bool)), SLOT(onInsert()));
   aBtnLayout->addWidget(myInsertBtn);
index abe6aa02599cb656bcd66a7f60d0044856004e3a..67c385b96ac26998277597270b60c0388f5b0c19 100644 (file)
@@ -163,11 +163,9 @@ void PartSet_IconFactory::processEvent(const std::shared_ptr<Events_Message>& th
       Events_Loop::loop()->eventByName(Config_FeatureMessage::GUI_EVENT())) {
     std::shared_ptr<Config_FeatureMessage> aFeatureMsg =
        std::dynamic_pointer_cast<Config_FeatureMessage>(theMessage);
-    if (!aFeatureMsg->isInternal()) {
-      ActionInfo aFeatureInfo;
-      aFeatureInfo.initFrom(aFeatureMsg);
-      // Remember features icons
-      myIcons[QString::fromStdString(aFeatureMsg->id())] = aFeatureInfo.iconFile;
-    }
+    ActionInfo aFeatureInfo;
+    aFeatureInfo.initFrom(aFeatureMsg);
+    // Remember features icons
+    myIcons[QString::fromStdString(aFeatureMsg->id())] = aFeatureInfo.iconFile;
   }
 }
\ No newline at end of file
index 5756d13d6ac0bed9fe297b7adfec9b362aa0ae31..def7233c95bf2e772f96617e2e05193eec996fc3 100644 (file)
@@ -35,7 +35,6 @@
 
 #include <ModuleBase_ISelection.h>
 #include <ModuleBase_Operation.h>
-#include <ModuleBase_OperationAction.h>
 #include <ModuleBase_OperationFeature.h>
 #include <ModuleBase_ViewerPrs.h>
 #include <ModuleBase_Tools.h>
@@ -313,8 +312,8 @@ void PartSet_MenuMgr::onLineDetach(QAction* theAction)
     XGUI_Workshop* aWorkshop = aConnector->workshop();
     ModuleBase_Operation* anOperation = myModule->workshop()->currentOperation();
 
-    ModuleBase_OperationAction* anOpAction = new ModuleBase_OperationAction(
-                                   tr("Detach %1").arg(aLine->data()->name().c_str()), myModule);
+    ModuleBase_Operation* anOpAction =
+      new ModuleBase_Operation(tr("Detach %1").arg(aLine->data()->name().c_str()), myModule);
     bool isSketchOp = PartSet_SketcherMgr::isSketchOperation(anOperation);
     XGUI_OperationMgr* anOpMgr = aConnector->workshop()->operationMgr();
     // the active nested sketch operation should be aborted unconditionally
@@ -373,11 +372,11 @@ void PartSet_MenuMgr::setAuxiliary(const bool isChecked)
 
   QAction* anAction = action("AUXILIARY_CMD");
   //SessionPtr aMgr = ModelAPI_Session::get();
-  ModuleBase_OperationAction* anOpAction = 0;
+  ModuleBase_Operation* anOpAction = 0;
   XGUI_ModuleConnector* aConnector = dynamic_cast<XGUI_ModuleConnector*>(myModule->workshop());
   XGUI_OperationMgr* anOpMgr = aConnector->workshop()->operationMgr();
   if (isUseTransaction) {
-    anOpAction = new ModuleBase_OperationAction(anAction->text(), myModule);
+    anOpAction = new ModuleBase_Operation(anAction->text(), myModule);
     bool isSketchOp = PartSet_SketcherMgr::isSketchOperation(anOperation);
 
     bool isCommitted;
index 35242846020d31c7ada2c2cffc3262705a957d33..6103aa898999282b5a5f49de882a0324f759df5a 100644 (file)
@@ -54,7 +54,6 @@
 #include <SketchPlugin_ConstraintCoincidence.h>
 
 #include <ModuleBase_Operation.h>
-#include <ModuleBase_OperationAction.h>
 #include <ModuleBase_IViewer.h>
 #include <ModuleBase_IViewWindow.h>
 #include <ModuleBase_IPropertyPanel.h>
@@ -249,6 +248,10 @@ PartSet_Module::PartSet_Module(ModuleBase_IWorkshop* theWshop)
   Config_PropManager::registerProp("Visualization", "sketch_dimension_color",
     "Dimension color",
     Config_Prop::Color, SKETCH_DIMENSION_COLOR);
+
+  Config_PropManager::registerProp("Shortcuts", "add_parameter_shortcut",
+    "Add parameter in parameters manager dialog",
+    Config_Prop::Shortcut, "Ctrl+A");
 }
 
 //******************************************************
@@ -451,9 +454,11 @@ void PartSet_Module::updateSketcherOnStart(ModuleBase_Operation* theOperation)
   if (PartSet_SketcherMgr::isSketchOperation(theOperation)) {
     mySketchMgr->startSketch(theOperation);
   }
-  else if (sketchMgr()->isNestedSketchOperation(theOperation)) {
-    mySketchMgr->startNestedSketch(theOperation);
-  }
+  // It is switched off because of
+  // Task #3067: 5.2.2 Drawing in the sketcher: change the mouse cursor arrow
+  //else if (sketchMgr()->isNestedSketchOperation(theOperation)) {
+  //  mySketchMgr->startNestedSketch(theOperation);
+  //}
 }
 
 //******************************************************
@@ -768,16 +773,14 @@ void PartSet_Module::propertyPanelDefined(ModuleBase_Operation* theOperation)
 }
 
 //******************************************************
-bool PartSet_Module::createWidgets(ModuleBase_Operation* theOperation,
+bool PartSet_Module::createWidgets(const FeaturePtr& theFeature, const QString& theXmlRepr,
                                    QList<ModuleBase_ModelWidget*>& theWidgets) const
 {
   bool aProcessed = false;
 
-  ModuleBase_OperationFeature* aFOperation =
-    dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
   XGUI_Workshop* aWorkshop = getWorkshop();
   XGUI_PropertyPanel* aPropertyPanel = aWorkshop->propertyPanel();
-  if (mySketchMgr->activeSketch().get() && aFOperation && aPropertyPanel) {
+  if (mySketchMgr->activeSketch().get() && aPropertyPanel) {
     ModuleBase_ISelection* aSelection = workshop()->selection();
     // click on a point in sketch leads here with the point is highlighted, not yet selected
     QList<ModuleBase_ViewerPrsPtr> aPreselection = aSelection->getHighlighted();
@@ -786,11 +789,10 @@ bool PartSet_Module::createWidgets(ModuleBase_Operation* theOperation,
       ObjectPtr anObject = aSelectedPrs->object();
 
       FeaturePtr aFeature = ModelAPI_Feature::feature(anObject);
-      FeaturePtr anOpFeature = aFOperation->feature();
       GeomShapePtr aShape = aSelectedPrs->shape();
       // click on the digit of dimension constrain comes here
       // with an empty shape, so we need the check
-      if (aFeature == anOpFeature && aShape.get() && !aShape->isNull()) {
+      if (aFeature == theFeature && aShape.get() && !aShape->isNull()) {
         // if feature has only one result and shape of result is equal to selected shape
         // this attribute is not processed. It is a case of Sketch Point.
         if (aFeature->results().size() == 1) {
@@ -802,8 +804,7 @@ bool PartSet_Module::createWidgets(ModuleBase_Operation* theOperation,
         AttributePtr anAttribute = PartSet_Tools::findAttributeBy2dPoint(anObject, aTDShape,
                                                                mySketchMgr->activeSketch());
         if (anAttribute.get()) {
-          QString aXmlRepr = aFOperation->getDescription()->xmlRepresentation();
-          ModuleBase_WidgetFactory aFactory(aXmlRepr.toStdString(), workshop());
+          ModuleBase_WidgetFactory aFactory(theXmlRepr.toStdString(), workshop());
 
           const std::string anAttributeId = anAttribute->id();
           aFactory.createWidget(aPropertyPanel->contentWidget(), anAttributeId);
@@ -987,7 +988,7 @@ bool PartSet_Module::deleteObjects()
 
     // 3. start operation
     QString aDescription = aWorkshop->contextMenuMgr()->action("DELETE_CMD")->text();
-    ModuleBase_OperationAction* anOpAction = new ModuleBase_OperationAction(aDescription, this);
+    ModuleBase_Operation* anOpAction = new ModuleBase_Operation(aDescription, this);
 
     // the active nested sketch operation should be aborted unconditionally
     // the Delete action should be additionally granted for the Sketch operation
index 5a6d25cc7276bcbddd215b37ade99819309574f9..bd91508616f0262bbe80aa3802e9b88f28b29ecf 100644 (file)
@@ -105,10 +105,11 @@ public:
   /// If there is found selected attribute, widgets are created and contains
   /// only a widget for the attribute
   /// It is important for Property Panel filling by sketch point attribute
-  /// \param theOperation a started operation
+  /// \param theFeature a feature of the started operation
+  /// \param theXmlRepr an XML representation of the operation
   /// \param theWidgets a list of created widgets
   /// \return boolean result, false by default
-  virtual bool createWidgets(ModuleBase_Operation* theOperation,
+  virtual bool createWidgets(const FeaturePtr& theFeature, const QString& theXmlRepr,
                              QList<ModuleBase_ModelWidget*>& theWidgets) const;
 
   /// Launching of a edit operation on the feature
index d6e5febf1fe4277ddffd2e5f912fd146f3e23df4..f0e4b1d43290943724342ae998bcd58cbf74b32e 100644 (file)
@@ -225,15 +225,17 @@ void PartSet_SketcherMgr::onEnterViewPort()
   return;
   #endif
 
-  if (canChangeCursor(getCurrentOperation())) {
-    QCursor* aCurrentCursor = QApplication::overrideCursor();
-    if (!aCurrentCursor || aCurrentCursor->shape() != Qt::CrossCursor) {
-      QApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
-#ifdef DEBUG_CURSOR
-      qDebug("onEnterViewPort() : Qt::CrossCursor");
-#endif
-    }
-  }
+  // It is switched off because of
+  // Task #3067: 5.2.2 Drawing in the sketcher: change the mouse cursor arrow
+  //  if (canChangeCursor(getCurrentOperation())) {
+  //    QCursor* aCurrentCursor = QApplication::overrideCursor();
+  //    if (!aCurrentCursor || aCurrentCursor->shape() != Qt::CrossCursor) {
+  //      QApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
+  //#ifdef DEBUG_CURSOR
+  //      qDebug("onEnterViewPort() : Qt::CrossCursor");
+  //#endif
+  //    }
+  //  }
 
   if (!isNestedCreateOperation(getCurrentOperation(), activeSketch()))
     return;
@@ -265,12 +267,12 @@ void PartSet_SketcherMgr::onLeaveViewPort()
   return;
   #endif
 
-  if (canChangeCursor(getCurrentOperation())) {
-    QApplication::restoreOverrideCursor();
-#ifdef DEBUG_CURSOR
-    qDebug("onLeaveViewPort() : None");
-#endif
-  }
+//  if (canChangeCursor(getCurrentOperation())) {
+//    QApplication::restoreOverrideCursor();
+//#ifdef DEBUG_CURSOR
+//    qDebug("onLeaveViewPort() : None");
+//#endif
+//  }
 
   if (!isNestedCreateOperation(getCurrentOperation(), activeSketch()))
     return;
@@ -1198,18 +1200,18 @@ void PartSet_SketcherMgr::stopSketch(ModuleBase_Operation* theOperation)
   workshop()->viewer()->set2dMode(false);
 }
 
-void PartSet_SketcherMgr::startNestedSketch(ModuleBase_Operation* theOperation)
-{
-  if (canChangeCursor(theOperation) && myIsMouseOverWindow) {
-    QCursor* aCurrentCursor = QApplication::overrideCursor();
-    if (!aCurrentCursor || aCurrentCursor->shape() != Qt::CrossCursor) {
-      QApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
-#ifdef DEBUG_CURSOR
-      qDebug("startNestedSketch() : Qt::CrossCursor");
-#endif
-    }
-  }
-}
+//void PartSet_SketcherMgr::startNestedSketch(ModuleBase_Operation* theOperation)
+//{
+//  if (canChangeCursor(theOperation) && myIsMouseOverWindow) {
+//    QCursor* aCurrentCursor = QApplication::overrideCursor();
+//    if (!aCurrentCursor || aCurrentCursor->shape() != Qt::CrossCursor) {
+//      QApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
+//#ifdef DEBUG_CURSOR
+//      qDebug("startNestedSketch() : Qt::CrossCursor");
+//#endif
+//    }
+//  }
+//}
 
 void PartSet_SketcherMgr::stopNestedSketch(ModuleBase_Operation* theOperation)
 {
@@ -1217,7 +1219,7 @@ void PartSet_SketcherMgr::stopNestedSketch(ModuleBase_Operation* theOperation)
   operationMgr()->onValidateOperation();
   // when sketch nested operation is stopped the cursor should be restored unconditionally
   //if (canChangeCursor(theOperation)) {
-    QApplication::restoreOverrideCursor();
+    //QApplication::restoreOverrideCursor();
 #ifdef DEBUG_CURSOR
     qDebug("stopNestedSketch() : None");
 #endif
index 9336fdbf2235f0f705e4b187cab07a8661c8681d..87e4a2865883d07c653bab75177e0554c9d724ac 100644 (file)
@@ -219,7 +219,7 @@ public:
 
   /// Starts sketch operation, connects to the opeation property panel
   /// \param theOperation a committed operation
-  void startNestedSketch(ModuleBase_Operation* theOperation);
+  //void startNestedSketch(ModuleBase_Operation* theOperation);
 
   /// Stop sketch operation, disconnects from the opeation property panel
   /// \param theOperation a stopped operation
index f4a880abf6a73a06673de76f797af8302c487f47..0e32b02850471f4c180c7ce07a36e824f3e526ce 100644 (file)
@@ -83,6 +83,9 @@ public:
   /// \return a boolean value
   virtual bool isValidSelection(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue);
 
+  /// Returns True in case if the widget contains useful information for inspection tool
+  virtual bool isInformative() const { return false; }
+
 protected:
   /// If there is no operation in current session, start operation for modify parameters
   /// \return true if the operation was not opened
index 2ba13e6bd49603d358cc91df161154b9997cddb8..9dbab08eb7c78655152a2f9d2606b876919c2295 100644 (file)
@@ -76,9 +76,9 @@
 #include <QCheckBox>
 #include <QGroupBox>
 #include <QPushButton>
-#include <QStackedWidget>
 #include <QLineEdit>
 #include <QDoubleValidator>
+#include <QDialog>
 
 #ifndef DBL_MAX
 #define DBL_MAX 1.7976931348623158e+308
@@ -113,6 +113,23 @@ myIsSelection(false)
   mySizeOfView->setValidator(aValidator);
   aSizeLayout->addWidget(mySizeOfView);
 
+  myPartSetMessage = new QDialog(this, Qt::ToolTip);
+  myPartSetMessage->setModal(false);
+  myPartSetMessage->setStyleSheet("background-color:lightyellow;");
+  QVBoxLayout* aMsgLay = new QVBoxLayout(myPartSetMessage);
+  QString aMsg = tr("The Sketch is created in PartSet.\n"
+    "It will be necessary to create a Part in order to use this sketch for body creation");
+  aMsgLay->addWidget(new QLabel(aMsg, myPartSetMessage));
+  myPartSetMessage->hide();
+
+  mySizeMessage = new QDialog(mySizeOfView, Qt::ToolTip);
+  mySizeMessage->setModal(false);
+  mySizeMessage->setStyleSheet("background-color:lightyellow;");
+  aMsgLay = new QVBoxLayout(mySizeMessage);
+  aMsg = tr("A size of Sketch view can be defined here.");
+  aMsgLay->addWidget(new QLabel(aMsg, mySizeMessage));
+  mySizeMessage->hide();
+
   QString aText = translate(theData->getProperty("title"));
   QLabel* aLabel = new QLabel(aText, aFirstWgt);
   aLabel->setWordWrap(true);
@@ -317,6 +334,10 @@ void PartSet_WidgetSketchLabel::updateByPlaneSelected(const ModuleBase_ViewerPrs
   GeomPlanePtr aPlane = plane();
   if (!aPlane.get())
     return;
+
+  myPartSetMessage->hide();
+  mySizeMessage->hide();
+
   // 1. hide main planes if they have been displayed and display sketch preview plane
   myPreviewPlanes->erasePreviewPlanes(myWorkshop);
 
@@ -556,6 +577,21 @@ void PartSet_WidgetSketchLabel::activateCustom()
     mySizeOfViewWidget->setVisible(false);
 }
 
+void PartSet_WidgetSketchLabel::showEvent(QShowEvent* theEvent)
+{
+  if (mySizeOfViewWidget->isVisible()) {
+    DocumentPtr aDoc = feature()->document();
+    DocumentPtr aModDoc = ModelAPI_Session::get()->moduleDocument();
+    if (aModDoc == aDoc) {
+      myPartSetMessage->move(mapToGlobal(geometry().bottomLeft()));
+      myPartSetMessage->show();
+    }
+    mySizeMessage->move(mySizeOfView->mapToGlobal(mySizeOfView->geometry().center()));
+    mySizeMessage->show();
+  }
+}
+
+
 void PartSet_WidgetSketchLabel::deactivate()
 {
   ModuleBase_WidgetValidated::deactivate();
index 0e19d74701a9568cb0e3fc2ff938b9a76b34a2eb..51ad42abaaac1c9da4f8fdbee92a65bf155f8936 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <TopoDS_Shape.hxx>
 
+#include <QStackedWidget>
 #include <QMap>
 
 class PartSet_PreviewPlanes;
@@ -42,6 +43,7 @@ class QCheckBox;
 class QStackedWidget;
 class QLineEdit;
 class QPushButton;
+class QDialog;
 
 /**
 * \ingroup Modules
@@ -110,6 +112,12 @@ public:
   /// \param thePrs a presentation
   static bool canFillSketch(const std::shared_ptr<ModuleBase_ViewerPrs>& thePrs);
 
+  /// If widgets has several panels then this method has to show a page which contains information
+  /// for current feature. By default does nothing
+  virtual void showInformativePage() {
+    if (myStackWidget) myStackWidget->setCurrentIndex(1);
+  }
+
 signals:
   /// Signal on plane selection
   void planeSelected(const std::shared_ptr<GeomAPI_Pln>& thePln);
@@ -194,6 +202,9 @@ protected:
   /// \param thePrs a presentation
   bool fillSketchPlaneBySelection(const std::shared_ptr<ModuleBase_ViewerPrs>& thePrs);
 
+
+  virtual void showEvent(QShowEvent* theEvent);
+
 private slots:
   /// A slot called on set sketch plane view
   void onSetPlaneView();
@@ -242,6 +253,9 @@ private:
 
   bool myOpenTransaction;
   bool myIsSelection;
+
+  QDialog* myPartSetMessage;
+  QDialog* mySizeMessage;
 };
 
 #endif
index 308e1e6f3929ba4b695a77e6065b8d0cd8ffa40d..5bd0d6c0b6cc60348c27786a3803a169112d49f3 100644 (file)
@@ -19,4 +19,7 @@
 """Package for Exchange plugin for the Parametric Geometry API of the Modeler.
 """
 
-from ExchangeAPI import addImport, exportToFile, exportToXAO
\ No newline at end of file
+from ExchangeAPI import addImport, exportToFile, exportToXAO
+from ExchangeAPI import exportPart, importPart
+
+from .tools import *
diff --git a/src/PythonAPI/model/exchange/tools.py b/src/PythonAPI/model/exchange/tools.py
new file mode 100644 (file)
index 0000000..a80db1b
--- /dev/null
@@ -0,0 +1,25 @@
+# Copyright (C) 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
+#
+
+import os
+
+def removeFile(theFilename):
+    try: os.remove(theFilename)
+    except OSError: pass
+    assert not os.path.exists(theFilename), "Cannot remove file {}".format(theFilename)
index 0ac188e119a9146604c8bf955da901dc15ef4daa..4c55e701d20e58e42095fd1eeb5903020d398705 100644 (file)
@@ -322,6 +322,14 @@ bool SHAPERGUI::activateModule(SUIT_Study* theStudy)
     XGUI_Displayer* aDisp = myWorkshop->displayer();
     QObjectPtrList aObjList = aDisp->displayedObjects();
 
+    //if (myHighlightPointAspect.IsNull()) {
+    //  Handle(AIS_Trihedron) aTrihedron = mySelector->viewer()->getTrihedron();
+    //  myHighlightPointAspect =
+    //    new Graphic3d_AspectMarker3d(aTrihedron->getHighlightPointAspect()->Aspect().operator*());
+    //}
+    if (myOldSelectionColor.size() == 0)
+      myOldSelectionColor = aDisp->selectionColor();
+
     AIS_ListOfInteractive aList;
     aContext->DisplayedObjects(aList);
     AIS_ListIteratorOfListOfInteractive aLIt;
@@ -400,6 +408,12 @@ bool SHAPERGUI::deactivateModule(SUIT_Study* theStudy)
   }
   // Delete selector because it has to be redefined on next activation
   if (mySelector) {
+    //if (!myHighlightPointAspect.IsNull()) {
+    //  Handle(AIS_Trihedron) aTrihedron = mySelector->viewer()->getTrihedron();
+    //  aTrihedron->getHighlightPointAspect()->SetAspect(myHighlightPointAspect);
+    //  myHighlightPointAspect.Nullify();
+    //}
+    myWorkshop->displayer()->setSelectionColor(myOldSelectionColor);
     myProxyViewer->setSelector(0);
     delete mySelector;
     mySelector = 0;
@@ -412,6 +426,8 @@ bool SHAPERGUI::deactivateModule(SUIT_Study* theStudy)
   aResMgr->setValue("Study", "store_positions", myIsStorePositions);
   getApp()->setEditEnabled(myIsEditEnabled);
 
+  myOldSelectionColor.clear();
+
   // Post-processing for LoadScriptId to remove created(if it was created) SALOME Object Browser
   disconnect(getApp()->action(LightApp_Application::UserID+1), SIGNAL(triggered(bool)),
              this, SLOT(onScriptLoaded()));
@@ -532,6 +548,12 @@ SHAPERGUI_OCCSelector* SHAPERGUI::createSelector(SUIT_ViewManager* theMgr)
 {
   if (theMgr->getType() == OCCViewer_Viewer::Type()) {
     OCCViewer_Viewer* aViewer = static_cast<OCCViewer_Viewer*>(theMgr->getViewModel());
+
+    //if (myHighlightPointAspect.IsNull()) {
+    //  Handle(AIS_Trihedron) aTrihedron = aViewer->getTrihedron();
+    //  myHighlightPointAspect =
+    //    new Graphic3d_AspectMarker3d(aTrihedron->getHighlightPointAspect()->Aspect().operator*());
+    //}
     SHAPERGUI_OCCSelector* aSelector = new SHAPERGUI_OCCSelector(aViewer,
                                                                  getApp()->selectionMgr());
 #ifdef SALOME_PATCH_FOR_CTRL_WHEEL
@@ -545,6 +567,12 @@ SHAPERGUI_OCCSelector* SHAPERGUI::createSelector(SUIT_ViewManager* theMgr)
       aSel->setEnabled(aSel == aSelector);
     }
     myProxyViewer->setSelector(aSelector);
+
+    if (myOldSelectionColor.size() == 0)
+      myOldSelectionColor = myWorkshop->displayer()->selectionColor();
+
+    std::vector<int> aColor = Config_PropManager::color("Visualization", "selection_color");
+    myWorkshop->displayer()->setSelectionColor(aColor);
     return aSelector;
   }
   return 0;
@@ -857,6 +885,11 @@ void SHAPERGUI::preferencesChanged(const QString& theSection, const QString& the
   }
   aProp->setValue(aValue);
 
+  if ((theSection == "Visualization") && (theParam == "selection_color")) {
+    std::vector<int> aColor = Config_PropManager::color("Visualization", "selection_color");
+    myWorkshop->displayer()->setSelectionColor(aColor);
+  }
+
   myWorkshop->displayer()->redisplayObjects();
 }
 
index 32b5035cde8ac7006a3e06d8ced4d768411a8c77..1c52678385d50ea1ab3a12f52032bbd8579d5f45 100644 (file)
@@ -277,6 +277,9 @@ private slots:
   QMap<QString, QIntList> myToolbars;
   QMap<QString, QIntList> myDefaultToolbars;
   bool myIsToolbarsModified;
+
+  std::vector<int> myOldSelectionColor;
+  Handle(Graphic3d_AspectMarker3d) myHighlightPointAspect;
 };
 
 #endif
index eafb7949e2b98189c3058555c3768378628342fd..199149f69c9cc6a6aa44669f8bd5eae722717892 100644 (file)
@@ -220,6 +220,9 @@ ADD_UNIT_TESTS(
   TestConstraintDistanceBehavior.py
   TestConstraintDistanceHorizontal.py
   TestConstraintDistanceVertical.py
+  TestConstraintDistanceZero.py
+  TestConstraintDistanceHorizontalZero.py
+  TestConstraintDistanceVerticalZero.py
   TestConstraintEqual.py
   TestConstraintEqualEllipse.py
   TestConstraintFixed.py
index 391c1016234ecb1702b235963af676b10f31544e..8503f6fe141f02665010aededb2be00a111fdfdb 100644 (file)
@@ -31,6 +31,7 @@
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_XY.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Dir.h>
 
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_AttributeInteger.h>
@@ -62,6 +63,11 @@ void SketchPlugin_ConstraintDistance::initAttributes()
   data()->addAttribute(SketchPlugin_ConstraintDistance::LOCATION_TYPE_ID(),
                        ModelAPI_AttributeInteger::typeId());
   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), LOCATION_TYPE_ID());
+
+  AttributePtr anAttr = data()->addAttribute(SketchPlugin_ConstraintDistance::DIRECTION_ID(),
+                                             GeomDataAPI_Dir::typeId());
+  anAttr->setIsArgument(false);
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), DIRECTION_ID());
 }
 
 void SketchPlugin_ConstraintDistance::colorConfigInfo(std::string& theSection, std::string& theName,
@@ -101,6 +107,20 @@ AISObjectPtr SketchPlugin_ConstraintDistance::getAISObject(AISObjectPtr thePrevi
   return anAIS;
 }
 
+static std::shared_ptr<GeomAPI_Lin2d> getLine(DataPtr theData, const std::string& theAttrName)
+{
+  FeaturePtr aLineFeature = SketcherPrs_Tools::getFeatureLine(theData, theAttrName);
+  if (!aLineFeature)
+    return GeomLine2dPtr();
+
+  std::shared_ptr<GeomDataAPI_Point2D> aStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::START_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::END_ID()));
+
+  return GeomLine2dPtr(new GeomAPI_Lin2d(aStart->x(), aStart->y(), aEnd->x(), aEnd->y()));
+}
+
 double SketchPlugin_ConstraintDistance::calculateCurrentDistance()
 {
   double aDistance = -1.;
@@ -112,25 +132,39 @@ double SketchPlugin_ConstraintDistance::calculateCurrentDistance()
   std::shared_ptr<GeomDataAPI_Point2D> aPointB =
       SketcherPrs_Tools::getFeaturePoint(aData, SketchPlugin_Constraint::ENTITY_B(), aPlane);
 
+  GeomPnt2dPtr aGeomPntA, aGeomPntB;
+  GeomLine2dPtr aLine;
   if (aPointA.get() && aPointB.get()) {  // both points
-    aDistance = aPointA->pnt()->distance(aPointB->pnt());
+    aGeomPntA = aPointA->pnt();
+    aGeomPntB = aPointB->pnt();
   } else {
-    FeaturePtr aLineFeature;
-    std::shared_ptr<SketchPlugin_Line> aLine;
     if (!aPointA.get() && aPointB.get()) {  //Line and point
-      aLineFeature = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
-      aLine = std::dynamic_pointer_cast<SketchPlugin_Line>(aLineFeature);
-      if (aLine.get()) {
-        aDistance = aLine->distanceToPoint(aPointB->pnt());
-      }
+      aLine = getLine(aData, SketchPlugin_Constraint::ENTITY_A());
+      aGeomPntB = aPointB->pnt();
+      aGeomPntA = aLine ? aLine->project(aGeomPntB) : GeomPnt2dPtr();
     } else if (aPointA.get() && !aPointB.get()) {  // Point and line
-      aLineFeature = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
-      aLine = std::dynamic_pointer_cast<SketchPlugin_Line>(aLineFeature);
-      if (aLine.get()) {
-        aDistance = aLine->distanceToPoint(aPointA->pnt());
-      }
+      aLine = getLine(aData, SketchPlugin_Constraint::ENTITY_B());
+      aGeomPntA = aPointA->pnt();
+      aGeomPntB = aLine ? aLine->project(aGeomPntA) : GeomPnt2dPtr();
     }
   }
+
+  if (aGeomPntA.get() && aGeomPntB.get()) {
+    aDistance = aGeomPntA->distance(aGeomPntB);
+    if (aDistance < tolerance)
+      aDistance = 0.0;
+  }
+
+  // keep the direction between arguments for processing of the zero distance
+  std::shared_ptr<GeomDataAPI_Dir> aPointPointDir =
+      std::dynamic_pointer_cast<GeomDataAPI_Dir>(attribute(DIRECTION_ID()));
+  if (aDistance > tolerance)
+    aPointPointDir->setValue(aGeomPntB->x() - aGeomPntA->x(), aGeomPntB->y() - aGeomPntA->y(), 0.0);
+  else if (aLine) {
+    GeomDir2dPtr aLineDir = aLine->direction();
+    aPointPointDir->setValue(-aLineDir->y(), aLineDir->x(), 0.0);
+  }
+
   return aDistance;
 }
 
@@ -170,7 +204,7 @@ void SketchPlugin_ConstraintDistance::attributeChanged(const std::string& theID)
     if (!aValueAttr->isInitialized()) {
       // only if it is not initialized, try to compute the current value
       double aDistance = calculateCurrentDistance();
-      if (aDistance > 0) { // set as value the length of updated references
+      if (aDistance >= 0) { // set as value the length of updated references
         aValueAttr->setValue(aDistance);
       }
     }
@@ -215,16 +249,16 @@ void SketchPlugin_ConstraintDistance::attributeChanged(const std::string& theID)
     } else
       return;
 
-    if (aEndPnt->distance(aStartPnt) < tolerance)
-      return;
-
     myFlyoutUpdate = true;
-    std::shared_ptr<GeomAPI_Dir2d> aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt)));
     std::shared_ptr<GeomAPI_XY> aFlyoutDir = aFlyoutPnt->xy()->decreased(aStartPnt);
-
-    double X = aFlyoutDir->dot(aLineDir->xy());
-    double Y = -aFlyoutDir->cross(aLineDir->xy());
-    aFlyoutAttr->setValue(X, Y);
+    if (aEndPnt->distance(aStartPnt) >= tolerance) {
+      std::shared_ptr<GeomAPI_Dir2d> aLineDir(new GeomAPI_Dir2d(aEndPnt->decreased(aStartPnt)));
+      double X = aFlyoutDir->dot(aLineDir->xy());
+      double Y = -aFlyoutDir->cross(aLineDir->xy());
+      aFlyoutAttr->setValue(X, Y);
+    }
+    else
+      aFlyoutAttr->setValue(aFlyoutDir->x(), aFlyoutDir->y());
     myFlyoutUpdate = false;
   }
 }
index 082fb84a897927910399878373ec109bfec7a2f5..9ce5afbe79d47bed7d1c0cabb612d7b0b7ed77e5 100644 (file)
@@ -64,6 +64,14 @@ class SketchPlugin_ConstraintDistance : public SketchPlugin_ConstraintBase
     return MY_SIGNED;
   }
 
+  /// \brief The direction from the first object to the second.
+  ///        To change distance value from zero to non-zero correctly.
+  inline static const std::string& DIRECTION_ID()
+  {
+    static const std::string MY_DIRECTION_ID("DistanceDirection");
+    return MY_DIRECTION_ID;
+  }
+
   /// attribute name of dimension location type
   inline static const std::string& LOCATION_TYPE_ID()
   {
index f01a332f296b0b90d751165547938b937376df3f..3e9e9fd394b413d15268d2706b820be04b9ec53e 100644 (file)
@@ -104,28 +104,6 @@ std::string SketchPlugin_Line::processEvent(const std::shared_ptr<Events_Message
 }
 // LCOV_EXCL_STOP
 
-double SketchPlugin_Line::distanceToPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
-{
-  double aDelta = 0;
-
-  std::shared_ptr<ModelAPI_Data> aData = data();
-  std::shared_ptr<GeomDataAPI_Point2D> aPoint1 =
-    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aData->attribute(START_ID()));
-  std::shared_ptr<GeomDataAPI_Point2D> aPoint2 =
-    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aData->attribute(END_ID()));
-
-  GeomAPI_Lin2d aLin2d(aPoint1->x(), aPoint1->y(), aPoint2->x(), aPoint2->y());
-
-  if (false/*projection*/) {  // TODO: if it has not been necessary, remove this block
-    std::shared_ptr<GeomAPI_Pnt2d> aResult = aLin2d.project(thePoint);
-    aDelta = aResult->distance(thePoint);
-  } else {  // distance
-    aDelta = aLin2d.distance(thePoint);
-  }
-
-  return aDelta;
-}
-
 const std::string& SketchPlugin_Line::getKind()
 {
   static std::string MY_KIND = SketchPlugin_Line::ID();
index 7a23944ae6260d0ab8714a83579e07ea033e931a..28d60dcd0ccab75233db146247cbfc8c9cc1a963 100644 (file)
@@ -80,10 +80,6 @@ class SketchPlugin_Line : public SketchPlugin_SketchEntity,
   /// if message has selected object
   virtual std::string processEvent(const std::shared_ptr<Events_Message>& theMessage);
 
-  /// Return the distance between the feature and the point
-  /// \param thePoint the point
-  double distanceToPoint(const std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
-
   /// Called on change of any argument-attribute of this object
   SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
 
index 9c247446cfeeebb6d666134c7616727e45c15c1f..692ac5d0ed2be3377aad617f6744f1ca75773a5d 100644 (file)
@@ -315,6 +315,7 @@ void SketchPlugin_Sketch::attributeChanged(const std::string& theID) {
           std::shared_ptr<GeomAPI_Dir> aYDir(new GeomAPI_Dir(aNormDir->cross(aTempDir)));
           std::shared_ptr<GeomAPI_Dir> aXDir(new GeomAPI_Dir(aYDir->cross(aNormDir)));
 
+          bool aWasBlocked = data()->blockSendAttributeUpdated(true);
           // update position of the sketch
           std::shared_ptr<GeomDataAPI_Point> anOrigin = std::dynamic_pointer_cast
             <GeomDataAPI_Point>(data()->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
@@ -325,7 +326,7 @@ void SketchPlugin_Sketch::attributeChanged(const std::string& theID) {
           std::shared_ptr<GeomDataAPI_Dir> aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
             data()->attribute(SketchPlugin_Sketch::DIRX_ID()));
           aDirX->setValue(aXDir);
-          std::shared_ptr<GeomAPI_Dir> aDir = aPlane->direction();
+          data()->blockSendAttributeUpdated(aWasBlocked, true);
         }
       }
     }
index 413331d7238e016769e7f86fc8a4af98e61818b4..f230ce1d4ade5583923676cac0376bb2371a206f 100644 (file)
@@ -44,7 +44,7 @@ SketchConstraintDistanceHorizontal_1.feature().real("ConstraintValue").setText(D
 model.do()
 
 # changing the parameter
-for param in range(-30, 31, 2):
+for param in range(-31, 30, 2):
     if param == 0:
         continue
     DistanceParam.setValue(param)
@@ -68,7 +68,7 @@ SketchConstraintDistanceVertical_1.feature().real("ConstraintValue").setText(Dis
 model.do()
 
 # changing the parameter
-for param in range(-30, 31, 2):
+for param in range(-31, 30, 2):
     if param == 0:
         continue
     DistanceParam.setValue(param)
@@ -94,8 +94,10 @@ model.do()
 for param in range(-30, 31, 2):
     DistanceParam.setValue(param)
     model.do()
-    if param <= 0:
+    if param < 0:
         assert SketchConstraintDistance_1.feature().error() != '', "ERROR: Sketch should not be valid due to negative distance value"
+    elif param == 0: # constraint is valid, but lead to degenerated geometry
+        assert Sketch_1.feature().error() != '', "ERROR: Sketch should not be valid due to negative distance value"
     else:
         dist = model.distancePointPoint(firstPoint, secondPoint)
         assert math.fabs(dist - math.fabs(param)) < TOLERANCE, "Incorrect distance {}, expected {}".format(dist, math.fabs(param))
index 1a6bcbfa1d035154aa5e25004750f27f565873ce..155f4d79eaa8ee6a76086042770caf461acfee96 100644 (file)
@@ -117,17 +117,14 @@ assert (model.dof(aSketchFeature) == 3)
 #=========================================================================
 # Change a distance value
 #=========================================================================
-d = DISTANCE1 + 20.
+d = DISTANCE1 + 21.
 dStep = -5.
 while d >= -30.:
     aSession.startOperation()
     DISTANCE1 = d
     aDistance.setValue(DISTANCE1)
     aSession.finishOperation()
-    if DISTANCE1 == 0:
-        assert(aHDist1.error() != "")
-    else:
-        assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
     d += dStep
 assert (model.dof(aSketchFeature) == 3)
 
@@ -154,18 +151,15 @@ assert (model.dof(aSketchFeature) == 2)
 # Change a distance value (check previous constraint is applied too)
 #=========================================================================
 d = DISTANCE2
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE2 = d
     aDistance.setValue(DISTANCE2)
     aSession.finishOperation()
-    if DISTANCE2 == 0:
-        assert(aHDist2.error() != "")
-    else:
-        assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
-        assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
-        assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    assert math.fabs(horizontalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+    assert math.fabs(aPoint1Coords.x() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected x = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+    assert math.fabs(horizontalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
     d += dStep
 assert (model.dof(aSketchFeature) == 2)
 
@@ -216,16 +210,13 @@ assert (model.dof(aSketchFeature) == 6)
 # Change a distance value
 #=========================================================================
 d = DISTANCE3
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE3 = d
     aDistance.setValue(DISTANCE3)
     aSession.finishOperation()
-    if DISTANCE3 == 0:
-        assert(aHDist3.error() != "")
-    else:
-        assert math.fabs(horizontalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aStartPoint, aEndPoint), DISTANCE3)
+    assert math.fabs(horizontalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(horizontalDistance(aStartPoint, aEndPoint), DISTANCE3)
     d += dStep
 assert (model.dof(aSketchFeature) == 6)
 
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceHorizontalZero.py b/src/SketchPlugin/Test/TestConstraintDistanceHorizontalZero.py
new file mode 100644 (file)
index 0000000..cccb7b9
--- /dev/null
@@ -0,0 +1,122 @@
+# Copyright (C) 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
+#
+
+"""
+    Test the zero value of the constraint "DistanceHorizontal"
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from SketchAPI import *
+
+__updated__ = "2019-10-22"
+TOLERANCE = 1.e-6
+
+class TestZeroDistanceHorizontal(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myLine1 = self.mySketch.addLine(10, 10, 45, 27.5)
+    self.myLine2 = self.mySketch.addLine(20, 15, 30, 40)
+    self.myLine3 = self.mySketch.addLine(10, 0, 10, 10)
+    model.do()
+    self.myDOF = 12
+
+  def tearDown(self):
+    model.end()
+    model.checkSketch(self.mySketch, self.myDOF)
+
+  def assertDistanceHorizontal(self, theObject1, theObject2, theDistance):
+    dist = theObject2.x() - theObject1.x()
+    self.assertTrue(math.fabs(dist - theDistance) < TOLERANCE, "Current distance = {}, reference = {}".format(dist, theDistance))
+    model.checkSketch(self.mySketch, self.myDOF)
+
+
+  def test_distance_positive_nzznz(self):
+    """ Test 1. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine2.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+
+  def test_distance_negative_nzznz(self):
+    """ Test 2. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 15)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.endPoint(), self.myLine2.endPoint(), -15)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.endPoint(), self.myLine2.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.endPoint(), self.myLine2.endPoint(), -10)
+
+  def test_distance_equal_znzz(self):
+    """ Test 3. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.endPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+  def test_distance_notequal_znzz(self):
+    """ Test 4. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setHorizontalDistance(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.startPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceHorizontal(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
index b4365147f67b06b0567650fafd1d9b46466b2b4f..8c0b1b6c384ed24730540c88a0dcf6d1b28e1a68 100644 (file)
@@ -117,17 +117,14 @@ assert (model.dof(aSketchFeature) == 3)
 #=========================================================================
 # Change a distance value
 #=========================================================================
-d = DISTANCE1 + 20.
+d = DISTANCE1 + 21.
 dStep = -5.
 while d >= -30.:
     aSession.startOperation()
     DISTANCE1 = d
     aDistance.setValue(DISTANCE1)
     aSession.finishOperation()
-    if DISTANCE1 == 0:
-        assert(aVDist1.error() != "")
-    else:
-        assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
     d += dStep
 assert (model.dof(aSketchFeature) == 3)
 
@@ -154,18 +151,15 @@ assert (model.dof(aSketchFeature) == 2)
 # Change a distance value (check previous constraint is applied too)
 #=========================================================================
 d = DISTANCE2
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE2 = d
     aDistance.setValue(DISTANCE2)
     aSession.finishOperation()
-    if DISTANCE2 == 0:
-        assert(aVDist2.error() != "")
-    else:
-        assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
-        assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
-        assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
+    assert math.fabs(verticalDistance(anExtCoords, aPoint1Coords) - DISTANCE2) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(anExtCoords, aPoint1Coords), DISTANCE2)
+    assert math.fabs(aPoint1Coords.y() - DISTANCE2) < 1.e-5, "Wrong point coordinates ({}, {}), expected y = {}".format(aPoint1Coords.x(), aPoint1Coords.y(), DISTANCE2)
+    assert math.fabs(verticalDistance(aPoint1Coords, aPoint2Coords) - DISTANCE1) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aPoint1Coords, aPoint2Coords), DISTANCE1)
     d += dStep
 assert (model.dof(aSketchFeature) == 2)
 
@@ -216,16 +210,13 @@ assert (model.dof(aSketchFeature) == 6)
 # Change a distance value
 #=========================================================================
 d = DISTANCE3
-dStep = -5.
+dStep = -7.
 while d >= -50.:
     aSession.startOperation()
     DISTANCE3 = d
     aDistance.setValue(DISTANCE3)
     aSession.finishOperation()
-    if DISTANCE3 == 0:
-        assert(aVDist3.error() != "")
-    else:
-        assert math.fabs(verticalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aStartPoint, aEndPoint), DISTANCE3)
+    assert math.fabs(verticalDistance(aStartPoint, aEndPoint) - DISTANCE3) < 1.e-5, "Distance values are different: {0} != {1}".format(verticalDistance(aStartPoint, aEndPoint), DISTANCE3)
     d += dStep
 assert (model.dof(aSketchFeature) == 6)
 
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceVerticalZero.py b/src/SketchPlugin/Test/TestConstraintDistanceVerticalZero.py
new file mode 100644 (file)
index 0000000..7578a72
--- /dev/null
@@ -0,0 +1,122 @@
+# Copyright (C) 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
+#
+
+"""
+    Test the zero value of the constraint "DistanceVertical"
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from SketchAPI import *
+
+__updated__ = "2019-10-22"
+TOLERANCE = 1.e-6
+
+class TestZeroDistanceVertical(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myLine1 = self.mySketch.addLine(10, 10, 45, 27.5)
+    self.myLine2 = self.mySketch.addLine(20, 15, 30, 40)
+    self.myLine3 = self.mySketch.addLine(0, 10, 10, 10)
+    model.do()
+    self.myDOF = 12
+
+  def tearDown(self):
+    model.end()
+    model.checkSketch(self.mySketch, self.myDOF)
+
+  def assertDistanceVertical(self, theObject1, theObject2, theDistance):
+    dist = theObject2.y() - theObject1.y()
+    self.assertTrue(math.fabs(dist - theDistance) < TOLERANCE, "Current distance = {}, reference = {}".format(dist, theDistance))
+    model.checkSketch(self.mySketch, self.myDOF)
+
+
+  def test_distance_positive_nzznz(self):
+    """ Test 1. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.startPoint(), self.myLine2.startPoint(), 5)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine2.startPoint(), 5)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine2.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine2.startPoint(), 10)
+
+  def test_distance_negative_nzznz(self):
+    """ Test 2. Change distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.endPoint(), self.myLine2.startPoint(), 12.5)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.endPoint(), self.myLine2.startPoint(), -12.5)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.endPoint(), self.myLine2.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(12.5)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.endPoint(), self.myLine2.startPoint(), -12.5)
+
+  def test_distance_equal_znzz(self):
+    """ Test 3. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.endPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+  def test_distance_notequal_znzz(self):
+    """ Test 4. Change distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setVerticalDistance(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.startPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistanceVertical(self.myLine1.startPoint(), self.myLine3.startPoint(), 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
diff --git a/src/SketchPlugin/Test/TestConstraintDistanceZero.py b/src/SketchPlugin/Test/TestConstraintDistanceZero.py
new file mode 100644 (file)
index 0000000..cf9a69d
--- /dev/null
@@ -0,0 +1,188 @@
+# Copyright (C) 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
+#
+
+"""
+    Test the zero value of the constraint "Distance"
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+from SketchAPI import *
+
+__updated__ = "2019-10-22"
+TOLERANCE = 1.e-6
+
+class TestZeroDistance(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myLine1 = self.mySketch.addLine(10, 10, 45, 27.5)
+    self.myLine2 = self.mySketch.addLine(20, 15, 30, 40)
+    self.myLine3 = self.mySketch.addLine(10, 0, 10, 10)
+    model.do()
+    self.myDOF = 12
+
+  def tearDown(self):
+    model.end()
+    model.checkSketch(self.mySketch, self.myDOF)
+
+  def assertDistance(self, theObject1, theObject2, theDistance, isSigned = False):
+    dist = -1.
+    if issubclass(type(theObject1), SketchAPI_SketchEntity):
+      if isSigned:
+        dist = model.signedDistancePointLine(theObject2, theObject1)
+      else:
+        dist = model.distancePointLine(theObject2, theObject1)
+    elif issubclass(type(theObject2), SketchAPI_SketchEntity):
+      if isSigned:
+        dist = model.signedDistancePointLine(theObject1, theObject2)
+      else:
+        dist = model.distancePointLine(theObject1, theObject2)
+    else:
+      dist = model.distancePointPoint(theObject1, theObject2)
+    self.assertTrue(math.fabs(dist - theDistance) < TOLERANCE, "Current distance = {}, reference = {}".format(dist, theDistance))
+    model.checkSketch(self.mySketch, self.myDOF)
+
+
+  def test_distance_points_nzznz(self):
+    """ Test 1. Change point-point distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 20)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 20)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    self.myDOF += 1
+    model.do()
+    self.assertDistance(self.myLine1.endPoint(), self.myLine2.endPoint(), 20)
+
+  def test_distance_points_znzz(self):
+    """ Test 2. Change point-point distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+    self.myDOF -= 2
+    model.do()
+    self.assertDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    self.myDOF += 1
+    model.do()
+    self.assertDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1.startPoint(), self.myLine3.endPoint(), 0)
+
+
+  def test_unsigned_distance_nzznz(self):
+    """ Test 3. Change unsigned point-line distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.result(), self.myLine2.endPoint(), 20)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 20)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 0)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 20)
+
+  def test_unsigned_distance_znzz(self):
+    """ Test 4. Change unsigned point-line distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setDistance(self.myLine2.startPoint(), self.myLine1.result(), 0)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+
+  def test_signed_distance_nzznz(self):
+    """ Test 5. Change signed point-line distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine1.result(), self.myLine2.endPoint(), 20, True)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), -20, True)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), 0, True)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine2.endPoint(), -20, True)
+
+  def test_signed_distance_nzznz_2(self):
+    """ Test 6. Change signed point-line distance from non-zero to zero and back to non-zero
+    """
+    dist = self.mySketch.setDistance(self.myLine3.startPoint(), self.myLine1.result(), 10, True)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine3.startPoint(), 10, True)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine3.startPoint(), 0, True)
+
+    SketchAPI_Constraint(dist).setValue(20)
+    model.do()
+    self.assertDistance(self.myLine1, self.myLine3.startPoint(), 20, True)
+
+  def test_signed_distance_znzz(self):
+    """ Test 7. Change signed point-line distance from zero to non-zero and back to zero
+    """
+    dist = self.mySketch.setDistance(self.myLine2.startPoint(), self.myLine1.result(), 0, True)
+    self.myDOF -= 1
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+    SketchAPI_Constraint(dist).setValue(10)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 10)
+
+    SketchAPI_Constraint(dist).setValue(0)
+    model.do()
+    self.assertDistance(self.myLine2.startPoint(), self.myLine1, 0)
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
index d1a9378d4162e996f16e2beaa445580045be0a2f..d2253d9aca8442b8cf8370ca8be6d1fccda1681f 100644 (file)
@@ -25,8 +25,7 @@
         <sketch-start-label id="External" geometrical_selection="true" title="Select a plane on which to create a sketch" tooltip="Select a plane on which to create a sketch">
           <validator id="GeomValidators_Face" parameters="plane"/>
         </sketch-start-label>
-        <!-- <label id="SolverDOF"/>  -->
-        <label id="SolverError" styleSheet="color : red; font : bold"/>
+        <undo_label id="SolverError" isHTML="true" />
         <validator id="SketchPlugin_SolverErrorValidator"/>
       </feature>
 
           <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityB"/>
           <validator id="PartSet_DifferentObjects"/>
           <validator id="GeomValidators_ShapeType" parameters="vertex,line"/>
-          <validator id="PartSet_DifferentPoints" parameters="ConstraintEntityB"/>
         </sketch_shape_selector>
         <sketch_shape_selector
           id="ConstraintEntityB"
           <validator id="SketchPlugin_DistanceAttr" parameters="ConstraintEntityA"/>
           <validator id="SketchPlugin_ExternalValidator" parameters="ConstraintEntityA"/>
           <validator id="GeomValidators_ShapeType" parameters="vertex,line"/>
-          <validator id="PartSet_DifferentPoints" parameters="ConstraintEntityA"/>
         </sketch_shape_selector>
         <sketch-2dpoint_flyout_selector id="ConstraintFlyoutValuePnt"  default="computed" internal="1" obligatory="0"/>
 
         <doublevalue_editor label="Value" tooltip="Distance" id="ConstraintValue" default="computed" min="0">
-          <validator id="GeomValidators_Positive"/>
+          <validator id="GeomValidators_Positive" parameters="-1.e-10"/>
         </doublevalue_editor>
 
         <module_choice id="LocationType"
         <sketch-2dpoint_flyout_selector id="ConstraintFlyoutValuePnt"  default="computed" internal="1" obligatory="0"/>
 
         <doublevalue_editor label="Value" tooltip="Distance" id="DistanceValue" default="computed" min="0">
-          <validator id="GeomValidators_Positive"/>
+          <validator id="GeomValidators_Positive" parameters="-1.e-10"/>
         </doublevalue_editor>
 
         <module_choice id="LocationType"
         <sketch-2dpoint_flyout_selector id="ConstraintFlyoutValuePnt"  default="computed" internal="1" obligatory="0"/>
 
         <doublevalue_editor label="Value" tooltip="Distance" id="DistanceValue" default="computed" min="0">
-          <validator id="GeomValidators_Positive"/>
+          <validator id="GeomValidators_Positive" parameters="-1.e-10"/>
         </doublevalue_editor>
 
         <module_choice id="LocationType"
index 6e6a6642f44f92a1eb6fd814da61c010fe203f37..de38dfcdb087bc79945b049645e5c84998a34ac9 100644 (file)
@@ -54,5 +54,5 @@ void PlaneGCSSolver_ConstraintWrapper::setValue(const double& theValue)
 
 double PlaneGCSSolver_ConstraintWrapper::value() const
 {
-  return myValueParam->value();
+  return myValueParam ? myValueParam->value() : 0.0;
 }
index 0850933a000bdfb5e2847c328412462f2e5c2c2b..29568114d22fe8d033ab6e7054ccdb5142fa6fcf 100644 (file)
@@ -25,6 +25,7 @@
 #include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_Storage.h>
 #include <PlaneGCSSolver_Tools.h>
+#include <PlaneGCSSolver_UpdateCoincidence.h>
 
 #include <SketchPlugin_ConstraintDistanceHorizontal.h>
 #include <SketchPlugin_ConstraintDistanceVertical.h>
@@ -71,6 +72,86 @@ static void adjustOddPoint(const EntityWrapperPtr& theDistPoint,
   *(theOddPoint->y) = aProjectedPnt->y();
 }
 
+static FeaturePtr getFeature(AttributeRefAttrPtr theRefAttr)
+{
+  ObjectPtr anObj;
+  if (theRefAttr->isObject())
+    anObj = theRefAttr->object();
+  else
+    anObj = theRefAttr->attr()->owner();
+  return ModelAPI_Feature::feature(anObj);
+}
+
+static void calculateDistanceDirection(const ConstraintPtr& theConstraint,
+                                       const StoragePtr& theStorage,
+                                       double& theDirX, double& theDirY)
+{
+  std::shared_ptr<GeomDataAPI_Dir> aDistDir = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+      theConstraint->attribute(SketchPlugin_ConstraintDistance::DIRECTION_ID()));
+  if (aDistDir && aDistDir->isInitialized()) {
+    theDirX = aDistDir->x();
+    theDirY = aDistDir->y();
+    if (fabs(theDirX) > tolerance || fabs(theDirY) > tolerance)
+      return;
+  }
+
+  AttributeRefAttrPtr aRefAttrA = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
+  AttributeRefAttrPtr aRefAttrB = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
+
+  EntityWrapperPtr aEntityA = theStorage->entity(aRefAttrA);
+  EntityWrapperPtr aEntityB = theStorage->entity(aRefAttrB);
+
+  GCSPointPtr aPoint;
+  if (aEntityA->type() != ENTITY_LINE && aEntityB->type() != ENTITY_LINE) {
+    aPoint = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(aEntityA)->point();
+    theDirX = 1.0;
+    theDirY = 0.0;
+
+    EdgeWrapperPtr anEdgeA = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(
+        theStorage->entity(getFeature(aRefAttrA)));
+    EdgeWrapperPtr anEdgeB = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(
+        theStorage->entity(getFeature(aRefAttrB)));
+
+    if (anEdgeA && anEdgeB) {
+      GCS::DeriVector2 aDirA = anEdgeA->entity()->CalculateNormal(*aPoint);
+      GCS::DeriVector2 aDirB = anEdgeB->entity()->CalculateNormal(*aPoint);
+      double x = -aDirA.x + aDirB.x;
+      double y = -aDirA.y + aDirB.y;
+      double norm = sqrt(x*x + y*y);
+      if (norm > tolerance) {
+        theDirX = x / norm;
+        theDirY = y / norm;
+      }
+    }
+  }
+}
+
+static void moveEntity(const ConstraintPtr& theConstraint,
+                       const StoragePtr& theStorage,
+                       const double theDX, const double theDY)
+{
+  static const double THE_SHIFT = 1.e-4;
+
+  AttributeRefAttrPtr aRefAttrA = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_A());
+  AttributeRefAttrPtr aRefAttrB = theConstraint->refattr(SketchPlugin_Constraint::ENTITY_B());
+
+  EntityWrapperPtr aEntityA = theStorage->entity(aRefAttrA);
+  EntityWrapperPtr aEntityB = theStorage->entity(aRefAttrB);
+
+  PointWrapperPtr aPointA = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(aEntityA);
+  PointWrapperPtr aPointB = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(aEntityB);
+
+  if (aPointA) {
+    *aPointA->point()->x -= THE_SHIFT * theDX;
+    *aPointA->point()->y -= THE_SHIFT * theDY;
+  }
+  else if (aPointB) {
+    *aPointB->point()->x += THE_SHIFT * theDX;
+    *aPointB->point()->y += THE_SHIFT * theDY;
+  }
+}
+
+
 
 void SketchSolver_ConstraintDistance::getAttributes(
     EntityWrapperPtr& theValue,
@@ -82,21 +163,30 @@ void SketchSolver_ConstraintDistance::getAttributes(
     return;
   }
 
+  ScalarWrapperPtr aValue = std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(theValue);
+  bool isCoincidence = fabs(aValue->value()) < tolerance;
+
   if (theAttributes[1]) {
     if (myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID())
       myType = CONSTRAINT_HORIZONTAL_DISTANCE;
     else if (myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID())
       myType = CONSTRAINT_VERTICAL_DISTANCE;
     else
-      myType = CONSTRAINT_PT_PT_DISTANCE;
+      myType = isCoincidence ? CONSTRAINT_PT_PT_COINCIDENT : CONSTRAINT_PT_PT_DISTANCE;
   } else if (theAttributes[2] && theAttributes[2]->type() == ENTITY_LINE)
-    myType = CONSTRAINT_PT_LINE_DISTANCE;
+    myType = isCoincidence ? CONSTRAINT_PT_ON_CURVE : CONSTRAINT_PT_LINE_DISTANCE;
   else
     theAttributes.clear();
 
+  if (myType == CONSTRAINT_HORIZONTAL_DISTANCE || myType == CONSTRAINT_VERTICAL_DISTANCE)
+    mySignValue = aValue->value() < 0.0 ? -1.0 : 1.0;
+
   myPrevValue = 0.0;
 
-  myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateFeature::GROUP());
+  if (isCoincidence)
+    myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
+  else
+    myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateFeature::GROUP());
 }
 
 void SketchSolver_ConstraintDistance::adjustConstraint()
@@ -127,7 +217,41 @@ void SketchSolver_ConstraintDistance::update()
   ConstraintWrapperPtr aConstraint = myStorage->constraint(myBaseConstraint);
   myPrevValue = aConstraint->value();
 
-  SketchSolver_Constraint::update();
+  bool isDistanceAlognDir =
+    myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID() ||
+    myBaseConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID();
+
+  AttributeDoublePtr aCurValue = myBaseConstraint->real(SketchPlugin_Constraint::VALUE());
+  bool isZeroSwitch = fabs(myPrevValue) < tolerance && fabs(aCurValue->value()) > tolerance;
+  bool isNonZeroSwitch = fabs(myPrevValue) > tolerance && fabs(aCurValue->value()) < tolerance;
+
+  if (!isDistanceAlognDir && (isZeroSwitch || isNonZeroSwitch)) {
+    // the value is changed from non-zero to zero or vice versa
+    remove();
+    process();
+
+    // move entities to avoid conflicting constraints
+    if (isZeroSwitch) {
+      double aDirX, aDirY;
+      // calculate the direction basing on the distanced objects
+      calculateDistanceDirection(myBaseConstraint, myStorage, aDirX, aDirY);
+      moveEntity(myBaseConstraint, myStorage, aDirX, aDirY);
+
+      if (myOddPoint) {
+        removeConstraintsKeepingSign();
+        addConstraintsToKeepSign();
+      }
+    }
+  }
+  else {
+    SketchSolver_Constraint::update();
+    if (isDistanceAlognDir && mySignValue * aConstraint->value() < 0.0) {
+      if (isZeroSwitch)
+        aConstraint->setValue(-aConstraint->value());
+      else
+        mySignValue *= -1.0;
+    }
+  }
 }
 
 bool SketchSolver_ConstraintDistance::remove()
index e6e3a002ce3f500f4e53583791224a85a4616f0b..f93711e819c9b075d53e350a1ed7cf4783e2d0a1 100644 (file)
@@ -33,16 +33,18 @@ class SketchSolver_Error
   /// The value parameter for the constraint
   inline static const std::string& CONSTRAINTS()
   {
-    static const std::string MY_ERROR_VALUE("The constraint is conflicting with others. "
-      "To fix this, you can either undo your operation or remove a conflicting constraint.");
+    static const std::string MY_ERROR_VALUE("<b>The constraint is conflicting with others. "
+      "To fix this, you can either <font color='red'>undo (Ctrl+Z)</font> your operation or "
+      "<font color='red'>remove</font> a conflicting constraint.</b>");
     return MY_ERROR_VALUE;
   }
   /// Cyclic dependency of copied features with their originals
   inline static const std::string& INFINITE_LOOP()
   {
     static const std::string MY_ERROR_VALUE(
-      "There is a circular reference between copied sketch entities and their originals. "
-      "To fix this, you can either undo your operation or remove wrong constraint.");
+      "<b>There is a circular reference between copied sketch entities and their originals. "
+      "To fix this, you can either <font color='red'>undo (Ctrl+Z)</font> your operation or "
+      "<font color='red'>remove</font> wrong constraint.</b>");
     return MY_ERROR_VALUE;
   }
   /// Constraints should use objects instead of features as attributes
index d4178917e8194b586d8d92e2403940328d23ba04..b15f0d05f0459ece2a964e17187769c4d103cbcb 100644 (file)
@@ -137,6 +137,20 @@ bool SketcherPrs_LengthDimension::IsReadyToDisplay(ModelAPI_Feature* theConstrai
   return readyToDisplay(theConstraint, thePlane, aPnt1, aPnt2);
 }
 
+static bool isEqualPoints(ModelAPI_Feature* theConstraint,
+                          const gp_Pnt& thePoint1,
+                          const gp_Pnt& thePoint2)
+{
+  bool isEqual = false;
+  if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID())
+    isEqual = Abs(thePoint1.X() - thePoint2.X()) < Precision::Confusion();
+  else if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID())
+    isEqual = Abs(thePoint1.Y() - thePoint2.Y()) < Precision::Confusion();
+  else
+    isEqual = thePoint1.SquareDistance(thePoint2) < Precision::SquareConfusion();
+  return isEqual;
+}
+
 void SketcherPrs_LengthDimension::Compute(
   const Handle(PrsMgr_PresentationManager3d)& thePresentationManager,
   const Handle(Prs3d_Presentation)& thePresentation,
@@ -147,6 +161,28 @@ void SketcherPrs_LengthDimension::Compute(
   gp_Pnt aPnt1, aPnt2;
   bool aReadyToDisplay = readyToDisplay(myConstraint, plane(), aPnt1, aPnt2);
   if (aReadyToDisplay) {
+    if (isEqualPoints(myConstraint, aPnt1, aPnt2)) {
+      // adjust points to draw the dimension presentation
+      std::shared_ptr<GeomDataAPI_Dir> aDirAttr = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+          myConstraint->attribute(SketchPlugin_ConstraintDistance::DIRECTION_ID()));
+      double x = 0.0, y = 0.0;
+      if (aDirAttr && aDirAttr->isInitialized()) {
+        x = aDirAttr->x();
+        y = aDirAttr->y();
+        if (x == 0.0 && y == 0.0)
+          x = 1.0;
+      }
+      else if (myConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID())
+        y = 1.0;
+      else
+        x = 1.0;
+      GeomPointPtr aCoord = plane()->to3D(x, y);
+
+      gp_XYZ aDir(aCoord->x(), aCoord->y(), aCoord->z());
+      aPnt1.ChangeCoord().Add(aDir * (-Precision::Confusion()));
+      aPnt2.ChangeCoord().Add(aDir * Precision::Confusion());
+    }
+
     myFirstPoint = aPnt1;
     mySecondPoint = aPnt2;
 
@@ -267,14 +303,14 @@ bool SketcherPrs_LengthDimension::readyToDisplay(ModelAPI_Feature* theConstraint
     if (!aPnt_A || !aPnt_B) // Objects not found
       return false;
 
-    if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID()) {
+    /*if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceHorizontal::ID()) {
       if (fabs(aPnt_A->x() - aPnt_B->x()) < Precision::Confusion())
         return false;
     }
     else if (theConstraint->getKind() == SketchPlugin_ConstraintDistanceVertical::ID()) {
       if (fabs(aPnt_A->y() - aPnt_B->y()) < Precision::Confusion())
         return false;
-    }
+    }*/
 
     // Get points from these object
     std::shared_ptr<GeomAPI_Pnt> aPoint1 = thePlane->to3D(aPnt_A->x(), aPnt_A->y());
index 4811fad69a911d85f107da142b2ebe3a520d9b33..9281220639283a9a36a46a9fd8cf6adc5bac2d4a 100644 (file)
@@ -199,6 +199,7 @@ ADD_DEFINITIONS( -DXGUI_EXPORTS ${OpenCASCADE_DEFINITIONS} -D_CRT_SECURE_NO_WARN
 SET(PROJECT_INCLUDES
     ${PROJECT_SOURCE_DIR}/src/Events
     ${PROJECT_SOURCE_DIR}/src/Config
+    ${PROJECT_SOURCE_DIR}/src/ExchangePlugin
     ${PROJECT_SOURCE_DIR}/src/ModelAPI
     ${PROJECT_SOURCE_DIR}/src/GeomAPI
     ${PROJECT_SOURCE_DIR}/src/ModuleBase
index 00cf24153ffbe8d7edd5e460e3d74bbe0ab5b0de..60db4013ccbd13c7982158734a7c1c0743faff2f 100644 (file)
@@ -55,7 +55,7 @@
 
 #include <ModuleBase_IModule.h>
 #include <ModuleBase_Tools.h>
-#include <ModuleBase_OperationAction.h>
+#include <ModuleBase_Operation.h>
 #include <ModuleBase_ViewerPrs.h>
 
 #include <QAction>
index c96dd3e7c5da6069654347f5919aa02fd7961f97..68549f2b2674fabf33d7421c29081dc9fd89e618 100644 (file)
@@ -121,7 +121,7 @@ QString qIntListInfo(const QIntList& theValues, const QString& theSeparator = QS
 //**************************************************************
 XGUI_Displayer::XGUI_Displayer(XGUI_Workshop* theWorkshop)
 : myWorkshop(theWorkshop), myNeedUpdate(false),
-  myViewerBlockedRecursiveCount(0), myIsFirstAISContextUse(true)
+  myViewerBlockedRecursiveCount(0), myContextId(0)
 {
 }
 
@@ -630,16 +630,19 @@ void XGUI_Displayer::updateViewer() const
 Handle(AIS_InteractiveContext) XGUI_Displayer::AISContext() const
 {
   Handle(AIS_InteractiveContext) aContext = myWorkshop->viewer()->AISContext();
-  if (!aContext.IsNull() && myIsFirstAISContextUse/*&& !aContext->HasOpenedContext()*/) {
-    XGUI_Displayer* aDisplayer = (XGUI_Displayer*)this;
-    aDisplayer->myIsFirstAISContextUse = false;
+  if (!aContext.IsNull() && (myContextId != aContext.get())) {
+    myContextId = aContext.get();
     if (!myWorkshop->selectionActivate()->isTrihedronActive())
       selectionActivate()->deactivateTrihedron(true);
     aContext->DefaultDrawer()->VIsoAspect()->SetNumber(0);
     aContext->DefaultDrawer()->UIsoAspect()->SetNumber(0);
 
-    ModuleBase_IViewer::DefaultHighlightDrawer = aContext->HighlightStyle();
+    //Handle(AIS_Trihedron) aTrihedron = myWorkshop->viewer()->trihedron();
+    //aTrihedron->getHighlightPointAspect()->SetScale(2.0);
+    //aTrihedron->getHighlightPointAspect()->SetTypeOfMarker(Aspect_TOM_O_STAR);
+
     // Commented out according to discussion in bug #2825
+    //ModuleBase_IViewer::DefaultHighlightDrawer = aContext->HighlightStyle();
     //Handle(Prs3d_Drawer) aSelStyle = aContext->SelectionStyle();
     //double aDeflection =
     //  QString(ModelAPI_ResultConstruction::DEFAULT_DEFLECTION().c_str()).toDouble();
@@ -653,6 +656,35 @@ Handle(AIS_InteractiveContext) XGUI_Displayer::AISContext() const
   return aContext;
 }
 
+//**************************************************************
+void XGUI_Displayer::setSelectionColor(const std::vector<int>& theColor)
+{
+  Handle(AIS_InteractiveContext) aContext = AISContext();
+  if (!aContext.IsNull()) {
+    Quantity_Color aQColor(theColor[0] / 255.,
+                           theColor[1] / 255.,
+                           theColor[2] / 255., Quantity_TOC_RGB);
+    aContext->SelectionStyle()->SetColor(aQColor);
+    aContext->HighlightStyle(Prs3d_TypeOfHighlight_LocalSelected)->SetColor(aQColor);
+  }
+}
+
+
+//**************************************************************
+std::vector<int> XGUI_Displayer::selectionColor() const
+{
+  std::vector<int> aColor;
+  Handle(AIS_InteractiveContext) aContext = AISContext();
+  if (!aContext.IsNull()) {
+    Quantity_Color aQColor = aContext->SelectionStyle()->Color();
+    aColor.push_back((int)(aQColor.Red() * 255));
+    aColor.push_back((int)(aQColor.Green() * 255));
+    aColor.push_back((int)(aQColor.Blue() * 255));
+  }
+  return aColor;
+}
+
+
 //**************************************************************
 Handle(SelectMgr_AndFilter) XGUI_Displayer::GetFilter()
 {
index b9d13642c220e6f43cca2977a50516589df4164b..938019db81bd118f053a96e2c4a5242ad6762a8c 100644 (file)
@@ -377,6 +377,13 @@ public:
   /// Returns scale of active view
   double getViewScale() const;
 
+  /// Set color of selection
+  /// \param theColor R,G,B values of color
+  void setSelectionColor(const std::vector<int>& theColor);
+
+  /// Returns current selection color
+  std::vector<int> selectionColor() const;
+
 signals:
   /// Signal on object display
   /// \param theObject a data object
@@ -463,7 +470,7 @@ private:
   /// Number of blocking of the viewer update. The viewer is updated only if it is zero
   int myViewerBlockedRecursiveCount;
 
-  bool myIsFirstAISContextUse; ///< Flag: first asking of AIS context: trihedron activation
+  mutable void* myContextId;
   mutable bool myNeedUpdate; ///< A flag that update was requested but not done
 };
 
index d92ae5669053b9799e40617da2307bf3e1d137fb..b8fba2d25fe7cbbb858df23cf91b8d805613aa40 100644 (file)
 #include "XGUI_FacesPanel.h"
 #include "XGUI_ObjectsBrowser.h"
 #include "XGUI_SelectionMgr.h"
+#include "XGUI_Selection.h"
 #include "XGUI_Tools.h"
 #include "XGUI_Workshop.h"
+#include "XGUI_Displayer.h"
+#include "XGUI_ViewerProxy.h"
 
 #include <ModuleBase_IModule.h>
 #include <ModuleBase_ISelection.h>
 #include <Config_PropManager.h>
 #include <Events_Loop.h>
 #include <GeomAlgoAPI_CompoundBuilder.h>
+#include <GeomAPI_Shape.h>
 
 #include <ModelAPI_Events.h>
+#include <ModelAPI_ResultGroup.h>
+#include <ModelAPI_AttributeSelectionList.h>
 
 #include <QAction>
 #include <QCheckBox>
@@ -49,7 +55,7 @@
 static const int LayoutMargin = 3;
 
 //********************************************************************
-XGUI_FacesPanel::XGUI_FacesPanel(QWidget* theParent, ModuleBase_IWorkshop* theWorkshop)
+XGUI_FacesPanel::XGUI_FacesPanel(QWidget* theParent, XGUI_Workshop* theWorkshop)
   : QDockWidget(theParent), myIsActive(false), myWorkshop(theWorkshop)
 {
   setWindowTitle(tr("Hide Faces"));
@@ -80,20 +86,34 @@ void XGUI_FacesPanel::reset(const bool isToFlushRedisplay)
   if (myLastItemIndex == 0) // do nothing because there was no activity in the pane after reset
     return;
 
-  // clear internal containers
-  myListView->getControl()->clear();
-  myItems.clear();
+  std::map<ObjectPtr, TopoDS_ListOfShape> anObjectToShapes;
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) > anObjectToPrs;
+  QMap<int, std::shared_ptr<ModuleBase_ViewerPrs> >::const_iterator aIt;
+  for (aIt = myItems.cbegin(); aIt != myItems.cend(); aIt++) {
+    getObjectsMapFromPrs(aIt.value(), anObjectToShapes, anObjectToPrs);
+  }
 
-  // restore previous view of presentations
-  bool isModified = redisplayObjects(myItemObjects);
-  std::set<std::shared_ptr<ModelAPI_Object> > aHiddenObjects = myHiddenObjects;
-  isModified = displayHiddenObjects(aHiddenObjects, myHiddenObjects) || isModified;
-  if (isModified)// && isToFlushRedisplay) // flush signal immediatelly until container is filled
+  std::set<ObjectPtr> aObjects;
+  TopoDS_ListOfShape anEmpty;
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs)>::const_iterator aPrsIt;
+  for (aPrsIt = anObjectToPrs.cbegin(); aPrsIt != anObjectToPrs.cend(); aPrsIt++) {
+    aObjects.insert(aPrsIt->first);
+    aPrsIt->second->setSubShapeHidden(anEmpty);
+  }
+  std::set<std::shared_ptr<ModelAPI_Object> >::const_iterator aGrpIt;
+  for (aGrpIt = myHiddenGroups.cbegin(); aGrpIt != myHiddenGroups.cend(); aGrpIt++)
+    (*aGrpIt)->setDisplayed(true);
+  myHiddenGroups.clear();
+
+  if (redisplayObjects(aObjects))
     flushRedisplay();
 
+  // clear internal containers
+  myListView->getControl()->clear();
+  myItems.clear();
   updateProcessedObjects(myItems, myItemObjects);
-  myHiddenObjects.clear();
   myLastItemIndex = 0; // it should be after redisplay as flag used in customize
+  myHiddenObjects.clear();
 }
 
 //********************************************************************
@@ -114,7 +134,8 @@ void XGUI_FacesPanel::selectionFilters(SelectMgr_ListOfFilter& theSelectionFilte
   ModuleBase_IModule* aModule = myWorkshop->module();
   QIntList aModuleSelectionFilters = myWorkshop->module()->selectionFilters();
 
-  theSelectionFilters.Append(aModule->selectionFilter(SF_GlobalFilter));
+  // The global filter makes problem for groups selection when any operation is launched
+  // theSelectionFilters.Append(aModule->selectionFilter(SF_GlobalFilter));
   theSelectionFilters.Append(aModule->selectionFilter(SF_FilterInfinite));
   theSelectionFilters.Append(aModule->selectionFilter(SF_ResultGroupNameFilter));
 }
@@ -146,7 +167,7 @@ void XGUI_FacesPanel::setActivePanel(const bool theIsActive)
     // selection should be cleared after emit of signal to do not process selection change
     // event by the previous selector
     // the selection is cleared by activating selection control
-    XGUI_Tools::workshop(myWorkshop)->selector()->clearSelection();
+    myWorkshop->selector()->clearSelection();
   }
   else
     emit deactivated();
@@ -158,6 +179,14 @@ bool XGUI_FacesPanel::useTransparency() const
   return myHiddenOrTransparent->isChecked();
 }
 
+//********************************************************************
+double XGUI_FacesPanel::transparency() const
+{
+  return useTransparency() ?
+    Config_PropManager::real("Visualization", "hidden_face_transparency") : 1;
+}
+
+
 //********************************************************************
 void XGUI_FacesPanel::restoreObjects(const std::set<ObjectPtr>& theHiddenObjects)
 {
@@ -209,48 +238,125 @@ bool XGUI_FacesPanel::processAction(ModuleBase_ActionType theActionType)
   }
 }
 
+//********************************************************************
+void XGUI_FacesPanel::getObjectsMapFromPrs(ModuleBase_ViewerPrsPtr thePrs,
+  std::map<ObjectPtr, TopoDS_ListOfShape>& theObjectToShapes,
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) >& theObjectToPrs)
+{
+  ObjectPtr anObject = thePrs->object();
+  if (!anObject.get())
+    return;
+
+  XGUI_Displayer* aDisplayer = myWorkshop->displayer();
+  ResultGroupPtr aResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anObject);
+  if (aResGroup.get()) {
+    // Process a grouip result
+    FeaturePtr aGroupFeature = ModelAPI_Feature::feature(aResGroup);
+    AttributeSelectionListPtr aSelectionList = aGroupFeature->selectionList("group_list");
+    AISObjectPtr aPrs;
+    for (int i = 0; i < aSelectionList->size(); i++) {
+      AttributeSelectionPtr aSelection = aSelectionList->value(i);
+      ResultPtr aRes = aSelection->context();
+      aPrs = aDisplayer->getAISObject(aRes);
+      if (aPrs.get()) {
+        Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(
+          aPrs->impl<Handle(AIS_InteractiveObject)>());
+        if (!aResultPrs.IsNull()) {
+          GeomShapePtr aShape = aSelection->value();
+          if (theObjectToShapes.find(aRes) != theObjectToShapes.end())
+            theObjectToShapes.at(aRes).Append(aShape->impl<TopoDS_Shape>());
+          else {
+            TopoDS_ListOfShape aListOfShapes;
+            aListOfShapes.Append(aShape->impl<TopoDS_Shape>());
+            theObjectToShapes[aRes] = aListOfShapes;
+            theObjectToPrs[aRes] = aResultPrs;
+          }
+        }
+      }
+    }
+  }
+  else {
+    // Process bodies
+    Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(
+      thePrs->interactive());
+    if (aResultPrs.IsNull())
+      return;
+
+    if (theObjectToShapes.find(anObject) != theObjectToShapes.end())
+      theObjectToShapes.at(anObject).Append(ModuleBase_Tools::getSelectedShape(thePrs));
+    else {
+      TopoDS_ListOfShape aListOfShapes;
+      aListOfShapes.Append(ModuleBase_Tools::getSelectedShape(thePrs));
+      theObjectToShapes[anObject] = aListOfShapes;
+      theObjectToPrs[anObject] = aResultPrs;
+    }
+  }
+}
+
 //********************************************************************
 void XGUI_FacesPanel::processSelection()
 {
-  QList<ModuleBase_ViewerPrsPtr> aSelected = myWorkshop->selection()->getSelected(
-                                                       ModuleBase_ISelection::Viewer);
+  QList<ModuleBase_ViewerPrsPtr> aSelected =
+    myWorkshop->selector()->selection()->getSelected(ModuleBase_ISelection::Viewer);
+
   bool isModified = false;
   static Events_ID aDispEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
 
-  std::map<ObjectPtr, NCollection_List<TopoDS_Shape> > anObjectToShapes;
+  std::map<ObjectPtr, TopoDS_ListOfShape> anObjectToShapes;
   std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) > anObjectToPrs;
+  std::set<int> aToRemove;
+
   for (int i = 0; i < aSelected.size(); i++) {
     ModuleBase_ViewerPrsPtr aPrs = aSelected[i];
     ObjectPtr anObject = aPrs->object();
     if (!anObject.get())
       continue;
-    if (ModuleBase_Tools::getSelectedShape(aPrs).ShapeType() != TopAbs_FACE)
-      continue;
 
-    Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(
-      aPrs->interactive());
-    if (aResultPrs.IsNull())
-      continue;
-    QString aItemName = XGUI_Tools::generateName(aPrs);
+    ResultGroupPtr aResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anObject);
+    if (!aResGroup.get()) {
+      GeomShapePtr aShapePtr = aPrs->shape();
+      if (!aShapePtr.get() || !aShapePtr->isFace())
+        return;
+    }
+
+    QString aItemName = aResGroup.get()?
+      aResGroup->data()->name().c_str() : XGUI_Tools::generateName(aPrs);
     if (myListView->hasItem(aItemName))
       return;
 
+    getObjectsMapFromPrs(aPrs, anObjectToShapes, anObjectToPrs);
+    if (aResGroup.get() && aResGroup->isDisplayed()) {
+      aResGroup->setDisplayed(false);
+      myHiddenGroups.insert(aResGroup);
+    }
+
+    // The code is dedicated to remove already selected items if they are selected twice
+    // It can happen in case of groups selection
+    QMap<int, ModuleBase_ViewerPrsPtr>::const_iterator aIt;
+    for (aIt = myItems.cbegin(); aIt != myItems.cend(); aIt++) {
+      ModuleBase_ViewerPrsPtr aPrs = aIt.value();
+      ObjectPtr aObject = aPrs->object();
+      ResultGroupPtr aResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(aObject);
+      if (aResGroup.get())
+        continue;
+      if (anObjectToShapes.find(aObject) != anObjectToShapes.end()) {
+        TopoDS_ListOfShape aShapes = anObjectToShapes[aObject];
+        GeomShapePtr aShapePtr = aPrs->shape();
+        if (aShapes.Contains(aShapePtr->impl<TopoDS_Shape>())) {
+          aToRemove.insert(aIt.key());
+        }
+      }
+    }
+
     myItems.insert(myLastItemIndex, aPrs);
     myListView->addItem(aItemName, myLastItemIndex);
     myLastItemIndex++;
     isModified = true;
-
-    if (anObjectToShapes.find(anObject) != anObjectToShapes.end())
-      anObjectToShapes.at(anObject).Append(ModuleBase_Tools::getSelectedShape(aPrs));
-    else {
-      NCollection_List<TopoDS_Shape> aListOfShapes;
-      aListOfShapes.Append(ModuleBase_Tools::getSelectedShape(aPrs));
-      anObjectToShapes[anObject] = aListOfShapes;
-      anObjectToPrs[anObject] = aResultPrs;
-    }
   }
-  for (std::map<ObjectPtr, NCollection_List<TopoDS_Shape> >::const_iterator
-    anIt = anObjectToShapes.begin(); anIt != anObjectToShapes.end(); anIt++) {
+
+  // Hide fully hidden shapes
+  std::map<ObjectPtr, TopoDS_ListOfShape>::const_iterator anIt;
+  for (anIt = anObjectToShapes.begin(); anIt != anObjectToShapes.end(); anIt++) {
     ObjectPtr anObject = anIt->first;
     if (!anObject.get() || anObjectToPrs.find(anObject) == anObjectToPrs.end())
       continue;
@@ -263,11 +369,35 @@ void XGUI_FacesPanel::processSelection()
     }
     ModelAPI_EventCreator::get()->sendUpdated(anObject, aDispEvent);
   }
+
+  // Process selected presentations
+  double aTransp = transparency();
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs)>::iterator aPrsIt;
+  for (aPrsIt = anObjectToPrs.begin(); aPrsIt != anObjectToPrs.end(); aPrsIt++) {
+    ObjectPtr anObject = aPrsIt->first;
+    Handle(ModuleBase_ResultPrs) aPrs = aPrsIt->second;
+    TopoDS_ListOfShape aAlreadyHidden = aPrs->hiddenSubShapes();
+    TopoDS_ListOfShape aListOfShapes = anObjectToShapes[anObject];
+    TopoDS_ListOfShape::Iterator aShapesIt(aListOfShapes);
+    for (; aShapesIt.More(); aShapesIt.Next()) {
+      if (!aAlreadyHidden.Contains(aShapesIt.Value()))
+        aAlreadyHidden.Append(aShapesIt.Value());
+    }
+    aPrs->setSubShapeHidden(aAlreadyHidden);
+    aPrs->setHiddenSubShapeTransparency(aTransp);
+  }
+
+  // Remove duplicate items
+  if (aToRemove.size() > 0) {
+    myListView->removeItems(aToRemove);
+    std::set<int>::const_iterator aIntIt;
+    for (aIntIt = aToRemove.cbegin(); aIntIt != aToRemove.cend(); aIntIt++)
+      myItems.remove(*aIntIt);
+  }
   if (isModified) {
     updateProcessedObjects(myItems, myItemObjects);
     flushRedisplay();
   }
-  onTransparencyChanged();
 }
 
 //********************************************************************
@@ -282,32 +412,50 @@ bool XGUI_FacesPanel::processDelete()
     return false;
 
   bool isModified = false;
-  std::set<ObjectPtr> aRestoredObjects;
-  for (std::set<int>::const_iterator anIt = aSelectedIds.begin(); anIt != aSelectedIds.end();
-       anIt++) {
+  std::set<ModuleBase_ViewerPrsPtr> aRestored;
+  std::set<int>::const_iterator anIt;
+  for (anIt = aSelectedIds.begin(); anIt != aSelectedIds.end(); anIt++) {
     ModuleBase_ViewerPrsPtr aPrs = myItems[*anIt];
-    if (aRestoredObjects.find(aPrs->object()) == aRestoredObjects.end())
-      aRestoredObjects.insert(aPrs->object());
-    myItems.remove(*anIt);
-    isModified = true;
+    if (aRestored.find(aPrs) == aRestored.end()) {
+      aRestored.insert(aPrs);
+      myItems.remove(*anIt);
+      isModified = true;
+    }
   }
-  if (isModified) {
-    bool isRedisplayed = redisplayObjects(aRestoredObjects);
-    isRedisplayed = displayHiddenObjects(aRestoredObjects, myHiddenObjects)
-                    || isRedisplayed;
-    if (isRedisplayed) {
-      flushRedisplay();
-      myWorkshop->viewer()->update();
+  std::map<ObjectPtr, TopoDS_ListOfShape> anObjectToShapes;
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) > anObjectToPrs;
+
+  std::set<ModuleBase_ViewerPrsPtr>::const_iterator aIt;
+  for (aIt = aRestored.cbegin(); aIt != aRestored.cend(); aIt++) {
+    getObjectsMapFromPrs((*aIt), anObjectToShapes, anObjectToPrs);
+    ResultGroupPtr aResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>((*aIt)->object());
+    if (aResGroup.get()) {
+      std::set<std::shared_ptr<ModelAPI_Object> >::iterator aGrpIt = myHiddenGroups.find(aResGroup);
+      if (aGrpIt != myHiddenGroups.end()) {
+        aResGroup->setDisplayed(true);
+        myHiddenGroups.erase(aGrpIt);
+      }
     }
-    // should be after flush of redisplay to have items object to be updated
-    updateProcessedObjects(myItems, myItemObjects);
+  }
 
+  std::set<ObjectPtr> aRestoredObjects;
+  std::map<ObjectPtr, TopoDS_ListOfShape>::const_iterator aShapesIt;
+  for (aShapesIt = anObjectToShapes.begin(); aShapesIt != anObjectToShapes.end(); aShapesIt++) {
+    TopoDS_ListOfShape aShapes = aShapesIt->second;
+    aRestoredObjects.insert(aShapesIt->first);
+    Handle(ModuleBase_ResultPrs) aPrs = anObjectToPrs[aShapesIt->first];
+    TopoDS_ListOfShape aHiddenShapes = aPrs->hiddenSubShapes();
+    TopoDS_ListOfShape::Iterator aSubShapesIt(aShapes);
+    for (; aSubShapesIt.More(); aSubShapesIt.Next()) {
+      if (aHiddenShapes.Contains(aSubShapesIt.Value()))
+        aHiddenShapes.Remove(aSubShapesIt.Value());
+    }
+    aPrs->setSubShapeHidden(aHiddenShapes);
   }
+  if (redisplayObjects(aRestoredObjects))
+    flushRedisplay();
 
   myListView->removeSelectedItems();
-  // Restore selection
-  myListView->restoreSelection(anIndices);
-  //appendSelectionInHistory();
   return true;
 }
 
@@ -332,79 +480,6 @@ bool XGUI_FacesPanel::redisplayObjects(
   return isModified;
 }
 
-//********************************************************************
-bool XGUI_FacesPanel::displayHiddenObjects(
-  const std::set<std::shared_ptr<ModelAPI_Object> >& theObjects,
-  std::set<std::shared_ptr<ModelAPI_Object> >& theHiddenObjects)
-{
-  if (theObjects.empty())
-    return false;
-
-  bool isModified = false;
-  static Events_ID aDispEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
-
-  for (std::set<ObjectPtr>::const_iterator anIt = theObjects.begin(); anIt != theObjects.end();
-       anIt++)
-  {
-    ObjectPtr anObject = *anIt;
-    // if the object was hidden by this panel
-    if (anObject->isDisplayed() || theHiddenObjects.find(anObject) == theHiddenObjects.end())
-      continue;
-    theHiddenObjects.erase(anObject);
-    anObject->setDisplayed(true); // it means that the object is hidden by hide all faces
-    ModelAPI_EventCreator::get()->sendUpdated(anObject, aDispEvent);
-    isModified = true;
-  }
-  return isModified;
-}
-
-//********************************************************************
-bool XGUI_FacesPanel::hideEmptyObjects()
-{
-  bool isModified = false;
-  static Events_ID aDispEvent = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
-  std::map<ObjectPtr, NCollection_List<TopoDS_Shape> > anObjectToShapes;
-  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) > anObjectToPrs;
-
-  for (QMap<int, ModuleBase_ViewerPrsPtr>::const_iterator anIt = myItems.begin();
-       anIt != myItems.end(); anIt++) {
-    ModuleBase_ViewerPrsPtr aPrs = anIt.value();
-    ObjectPtr anObject = aPrs->object();
-    if (!anObject.get() || !anObject->isDisplayed())
-      continue;
-
-    Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(
-      aPrs->interactive());
-    if (aResultPrs.IsNull())
-      continue;
-
-    if (anObjectToShapes.find(anObject) != anObjectToShapes.end())
-      anObjectToShapes.at(anObject).Append(ModuleBase_Tools::getSelectedShape(aPrs));
-    else {
-      NCollection_List<TopoDS_Shape> aListOfShapes;
-      aListOfShapes.Append(ModuleBase_Tools::getSelectedShape(aPrs));
-      anObjectToShapes[anObject] = aListOfShapes;
-      anObjectToPrs[anObject] = aResultPrs;
-    }
-  }
-  for (std::map<ObjectPtr, NCollection_List<TopoDS_Shape> >::const_iterator
-    anIt = anObjectToShapes.begin(); anIt != anObjectToShapes.end(); anIt++) {
-    ObjectPtr anObject = anIt->first;
-    if (!anObject.get() || anObjectToPrs.find(anObject) == anObjectToPrs.end())
-      continue;
-    Handle(ModuleBase_ResultPrs) aResultPrs = anObjectToPrs.at(anObject);
-
-    if (!aResultPrs->hasSubShapeVisible(anIt->second)) {
-      // erase object because it is entirely hidden
-      anObject->setDisplayed(false);
-      myHiddenObjects.insert(anObject);
-      ModelAPI_EventCreator::get()->sendUpdated(anObject, aDispEvent);
-      isModified = true;
-    }
-  }
-  return isModified;
-}
-
 //********************************************************************
 void XGUI_FacesPanel::updateProcessedObjects(QMap<int, ModuleBase_ViewerPrsPtr> theItems,
                                              std::set<ObjectPtr>& theObjects)
@@ -414,9 +489,23 @@ void XGUI_FacesPanel::updateProcessedObjects(QMap<int, ModuleBase_ViewerPrsPtr>
        anIt != theItems.end(); anIt++) {
     ModuleBase_ViewerPrsPtr aPrs = anIt.value();
     ObjectPtr anObject = aPrs.get() ? aPrs->object() : ObjectPtr();
-    if (anObject.get() && theObjects.find(anObject) != theObjects.end())
-      continue;
-    theObjects.insert(anObject);
+    if (anObject.get()) {
+      ResultGroupPtr aResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anObject);
+      if (aResGroup.get()) {
+        FeaturePtr aGroupFeature = ModelAPI_Feature::feature(aResGroup);
+        AttributeSelectionListPtr aSelectionList = aGroupFeature->selectionList("group_list");
+        for (int i = 0; i < aSelectionList->size(); i++) {
+          AttributeSelectionPtr aSelection = aSelectionList->value(i);
+          ResultPtr aRes = aSelection->context();
+          if (theObjects.find(aRes) == theObjects.end())
+            theObjects.insert(aRes);
+        }
+      }
+      else {
+        if (theObjects.find(anObject) == theObjects.end())
+          theObjects.insert(anObject);
+      }
+    }
   }
 }
 
@@ -429,42 +518,9 @@ void XGUI_FacesPanel::closeEvent(QCloseEvent* theEvent)
 
 //********************************************************************
 bool XGUI_FacesPanel::customizeObject(const ObjectPtr& theObject,
-                                      const AISObjectPtr& thePresentation)
+  const AISObjectPtr& thePresentation)
 {
-  if (myLastItemIndex == 0) // do nothing because there was no activity in the pane after reset
-    return false;
-
-  if (thePresentation.get() == NULL)
-    return false;
-
-  if (myItemObjects.find(theObject) == myItemObjects.end()) // not found
-    return false;
-
-  // if the object is displayed, the hidden faces are collected and set to the presentation
-  bool isModified = false;
-  NCollection_List<TopoDS_Shape> aHiddenSubShapes;
-  for (QMap<int, ModuleBase_ViewerPrsPtr>::const_iterator anIt = myItems.begin();
-    anIt != myItems.end(); anIt++) {
-    ModuleBase_ViewerPrsPtr aPrs = anIt.value();
-    if (aPrs.get() && aPrs->object() != theObject)
-      continue;
-    TopoDS_Shape aShape = ModuleBase_Tools::getSelectedShape(aPrs);
-    if (!aHiddenSubShapes.Contains(aShape))
-      aHiddenSubShapes.Append(aShape);
-  }
-
-  Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(
-    thePresentation->impl<Handle(AIS_InteractiveObject)>());
-  if (aResultPrs.IsNull())
-    return false;
-
-  isModified = aResultPrs->setSubShapeHidden(aHiddenSubShapes);
-
-  double aTransparency = !useTransparency() ? 1
-    : Config_PropManager::real("Visualization", "hidden_face_transparency");
-  isModified = aResultPrs->setHiddenSubShapeTransparency(aTransparency) || isModified;
-
-  return isModified;
+  return myItems.size() > 0;
 }
 
 //********************************************************************
@@ -476,16 +532,21 @@ void XGUI_FacesPanel::onDeleteItem()
 //********************************************************************
 void XGUI_FacesPanel::onTransparencyChanged()
 {
-  bool isModified = false;
-  if (useTransparency()) {
-    std::set<std::shared_ptr<ModelAPI_Object> > aHiddenObjects = myHiddenObjects;
-    isModified = displayHiddenObjects(aHiddenObjects, myHiddenObjects);
+  std::map<ObjectPtr, TopoDS_ListOfShape> anObjectToShapes;
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) > anObjectToPrs;
+  QMap<int, std::shared_ptr<ModuleBase_ViewerPrs> >::const_iterator aIt;
+  for (aIt = myItems.cbegin(); aIt != myItems.cend(); aIt++) {
+    getObjectsMapFromPrs(aIt.value(), anObjectToShapes, anObjectToPrs);
   }
-  else
-    isModified = hideEmptyObjects();
 
-  isModified = redisplayObjects(myItemObjects) || isModified;
-  if (isModified)
+  double aTransp = Config_PropManager::real("Visualization", "hidden_face_transparency");
+  std::set<ObjectPtr> aObjects;
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs)>::const_iterator aPrsIt;
+  for (aPrsIt = anObjectToPrs.cbegin(); aPrsIt != anObjectToPrs.cend(); aPrsIt++) {
+    aObjects.insert(aPrsIt->first);
+    aPrsIt->second->setHiddenSubShapeTransparency(useTransparency()? aTransp : 1);
+  }
+  if (redisplayObjects(aObjects))
     flushRedisplay();
 }
 
@@ -501,7 +562,8 @@ void XGUI_FacesPanel::flushRedisplay() const
 {
   Events_Loop::loop()->flush(Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY));
   // Necessary for update visibility icons in ObjectBrowser
-  XGUI_ObjectsBrowser* anObjectBrowser = XGUI_Tools::workshop(myWorkshop)->objectBrowser();
+  XGUI_ObjectsBrowser* anObjectBrowser = myWorkshop->objectBrowser();
   if (anObjectBrowser)
     anObjectBrowser->updateAllIndexes();
+  myWorkshop->viewer()->update();
 }
index d4a0b04dae171cd5badcb5fba7e4715ee69f0c63..68a3ce53bd71b7be1b39341eb9648767052f85d7 100644 (file)
 #include <ModuleBase_ActionType.h>
 #include <ModuleBase_Definitions.h>
 #include <ModuleBase_ViewerPrs.h>
+#include <ModuleBase_ResultPrs.h>
 
 #include <SelectMgr_ListOfFilter.hxx>
+#include <TopoDS_Shape.hxx>
 
 #include <QDockWidget>
 #include <QObject>
@@ -40,7 +42,7 @@ class AIS_InteractiveObject;
 
 class GeomAPI_AISObject;
 
-class ModuleBase_IWorkshop;
+class XGUI_Workshop;
 class ModuleBase_ListView;
 
 class QAction;
@@ -69,7 +71,7 @@ class XGUI_EXPORT XGUI_FacesPanel : public QDockWidget
 public:
   /// Constructor
   /// \param theParent is a parent of the property panel
-  XGUI_FacesPanel(QWidget* theParent, ModuleBase_IWorkshop* theWorkshop);
+  XGUI_FacesPanel(QWidget* theParent, XGUI_Workshop* theWorkshop);
   ~XGUI_FacesPanel() {}
 
   /// Clear content of list widget
@@ -95,10 +97,6 @@ public:
   /// \param theIsActive state whether the panel should be activated or deactivated
   void setActivePanel(const bool theIsActive);
 
-  /// Returns true if transparency choice is checked
-  /// \return boolean value
-  bool useTransparency() const;
-
   /// Returns true if the object is in internal container of hidden objects by this panel
   /// \param theObject a checked object
   /// \return boolean value
@@ -135,6 +133,10 @@ public:
   bool customizeObject(const std::shared_ptr<ModelAPI_Object>& theObject,
     const std::shared_ptr<GeomAPI_AISObject>& thePresentation);
 
+
+  XGUI_Workshop* workshop() const { return myWorkshop; }
+
+
 protected:
   /// Reimplementation to emit a signal about the panel close
   virtual void closeEvent(QCloseEvent* theEvent);
@@ -153,23 +155,28 @@ private:
   /// \return true if some of objects was redisplayed
   static bool redisplayObjects(const std::set<std::shared_ptr<ModelAPI_Object> >& theObjects);
 
-  /// Display objects if the objects are in a container of hidden by this pane.
-  /// \param theObjects container of objects
-  /// \param theHiddenObjects hidden objects, if object is in the container, it should be removed
-  /// \return true if some of objects was redisplayed
-  static bool displayHiddenObjects(const std::set<std::shared_ptr<ModelAPI_Object> >& theObjects,
-                                   std::set<std::shared_ptr<ModelAPI_Object> >& theHiddenObjects);
-
-  /// Iterates by items and hide objects where all sub-shapes are hidden
-  /// \return true if some of objects was redisplayed
-  bool hideEmptyObjects();
-
   /// Container of objects participating in the panel, it is filled by internal container
   /// \param theItems container of selected values
   /// \param theObjects [out] container of objects
   static void updateProcessedObjects(QMap<int, std::shared_ptr<ModuleBase_ViewerPrs> > theItems,
                                      std::set<std::shared_ptr<ModelAPI_Object> >& theObjects);
 
+  /// Returns maps of shapes and presentations. If object is a body result then it returns
+  /// its ruslts. If it is a group then it returns result of shapes included into the gropup
+  /// The function doesn't clear content of input maps.
+  /// \param thePrs a selected presintation
+  /// \param theObjectsToShapes map of objects to shapes list
+  /// \param theObjectToPrs map of objects to presentations
+  void getObjectsMapFromPrs(ModuleBase_ViewerPrsPtr thePrs,
+    std::map<ObjectPtr, TopoDS_ListOfShape>& theObjectsToShapes,
+    std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) >& theObjectToPrs);
+
+  /// Returns true if transparency choice is checked
+  /// \return boolean value
+  bool useTransparency() const;
+
+  double transparency() const;
+
 protected slots:
   /// Deletes element in list of items
   void onDeleteItem();
@@ -188,14 +195,15 @@ private:
 protected:
   QCheckBox* myHiddenOrTransparent; ///< if checked - transparent, else hidden
   ModuleBase_ListView* myListView; ///< list control of processed faces
-  ModuleBase_IWorkshop* myWorkshop; ///< workshop
+  XGUI_Workshop* myWorkshop; ///< workshop
 
   bool myIsActive; ///< current state about the panel is active
   int myLastItemIndex; ///< last index to be used in the map of items for the next added item
 
-  QMap<int, std::shared_ptr<ModuleBase_ViewerPrs> > myItems; ///< selected face items
+  QMap<int, ModuleBase_ViewerPrsPtr> myItems; ///< selected face items
   std::set<std::shared_ptr<ModelAPI_Object> > myItemObjects; ///< cached objects of myItems
   std::set<std::shared_ptr<ModelAPI_Object> > myHiddenObjects; ///< hidden objects
+  std::set<std::shared_ptr<ModelAPI_Object> > myHiddenGroups; ///< hidden objects
 };
 
 #endif
\ No newline at end of file
index 6d615ac06dbe53dce38181ce38f964017dc631b9..038f5f172136a2af8b199280c6d27b7c34a7e56d 100644 (file)
 //
 
 #include "XGUI_InspectionPanel.h"
+#include "XGUI_Workshop.h"
 #include "XGUI_SelectionMgr.h"
 #include "XGUI_Selection.h"
 #include "XGUI_Tools.h"
+#include "XGUI_ModuleConnector.h"
 
 #include <ModuleBase_ViewerPrs.h>
 #include <ModuleBase_Tools.h>
+#include <ModuleBase_OperationDescription.h>
+#include <ModuleBase_WidgetFactory.h>
+#include <ModuleBase_IModule.h>
+#include <ModuleBase_PageWidget.h>
+
 #include <ModelAPI_ResultField.h>
 
 #include <ModelAPI_Result.h>
@@ -54,6 +61,7 @@
 #include <QTextBrowser>
 #include <QResizeEvent>
 #include <QSplitter>
+#include <QStackedWidget>
 
 #include <BRepBndLib.hxx>
 #include <TopoDS_Iterator.hxx>
@@ -61,6 +69,7 @@
 #include <TopTools_ListOfShape.hxx>
 #include <Standard_ErrorHandler.hxx> // CAREFUL ! position of this file is critic
 
+
 // ================     Auxiliary functions     ================
 #define TITLE(val) ("<b>" + (val) + "</b>")
 
@@ -108,15 +117,18 @@ static void appendNamedValueToParameters(const QString& theName,
 
 // ================     XGUI_InspectionPanel    ================
 
-XGUI_InspectionPanel::XGUI_InspectionPanel(QWidget* theParent, XGUI_SelectionMgr* theMgr)
+XGUI_InspectionPanel::XGUI_InspectionPanel(QWidget* theParent, XGUI_Workshop* theWorkshop)
   : QDockWidget(theParent),
-  mySelectionMgr(theMgr)
+  myWorkshop(theWorkshop)
 {
   setWindowTitle(tr("Inspection Panel"));
   setObjectName(INSPECTION_PANEL);
   setStyleSheet("::title { position: relative; padding-left: 5px; text-align: left center }");
 
-  QSplitter* aSplitter = new QSplitter(Qt::Vertical, this);
+  myStackWgt = new QStackedWidget(this);
+
+  // Create shape selection page
+  QSplitter* aSplitter = new QSplitter(Qt::Vertical, myStackWgt);
 
   // Create an internal widget
   QWidget* aNameWgt = new QWidget(aSplitter);
@@ -184,9 +196,23 @@ XGUI_InspectionPanel::XGUI_InspectionPanel(QWidget* theParent, XGUI_SelectionMgr
   aSizes << 10 << 140 << 10;
   aSplitter->setSizes(aSizes);
 
-  setWidget(aSplitter);
+  myShapePanelId = myStackWgt->addWidget(aSplitter);
+
+  // Create feature selection page
+  QScrollArea* aScroll = new QScrollArea(myStackWgt);
+  aScroll->setWidgetResizable(true);
+  aScroll->setFrameStyle(QFrame::NoFrame);
+
+  myFeaturePane = new ModuleBase_PageWidget(aScroll);
+  myFeatureLayout = new QGridLayout(myFeaturePane);
+  myFeatureLayout->setContentsMargins(3, 3, 3, 3);
+  aScroll->setWidget(myFeaturePane);
+  //myFeaturePane->setEnabled(false);
 
-  connect(mySelectionMgr, SIGNAL(selectionChanged()), SLOT(onSelectionChanged()));
+  myFeaturePanelId = myStackWgt->addWidget(aScroll);
+
+  setWidget(myStackWgt);
+  connect(myWorkshop->selector(), SIGNAL(selectionChanged()), SLOT(onSelectionChanged()));
 }
 
 //********************************************************************
@@ -209,20 +235,21 @@ void XGUI_InspectionPanel::clearContent()
   }
   myTypeLbl->setText("");
   setParamsText("");
+
+  myFeaturePane->clearPage();
 }
 
 //********************************************************************
 void XGUI_InspectionPanel::onSelectionChanged()
 {
+  if (!isVisible())
+    return;
+
   clearContent();
-  XGUI_Selection* aSelection = mySelectionMgr->selection();
-  QList<ModuleBase_ViewerPrsPtr> aSelectedList =
-    aSelection->getSelected(ModuleBase_ISelection::Viewer);
 
-  QList<ModuleBase_ViewerPrsPtr> anOBSelected =
-    aSelection->getSelected(ModuleBase_ISelection::Browser);
-  if (!anOBSelected.isEmpty())
-    ModuleBase_ISelection::appendSelected(anOBSelected, aSelectedList);
+  XGUI_Selection* aSelection = myWorkshop->selector()->selection();
+  QList<ModuleBase_ViewerPrsPtr> aSelectedList =
+    aSelection->getSelected(myWorkshop->selector()->placeOfSelection());
 
   if (aSelectedList.count() > 0) {
     ModuleBase_ViewerPrsPtr aPrs = aSelectedList.first();
@@ -230,25 +257,33 @@ void XGUI_InspectionPanel::onSelectionChanged()
       std::dynamic_pointer_cast<ModelAPI_ResultField::ModelAPI_FieldStep>(aPrs->object());
     if (aStep)
       return;
-    TopoDS_Shape aShape = ModuleBase_Tools::getSelectedShape(aPrs);
-    if (aShape.IsNull()) {
-      ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(aPrs->object());
-      if (aRes.get()) {
-        GeomShapePtr aShpPtr = aRes->shape();
-        if (aShpPtr.get()) {
-          aShape = aShpPtr->impl<TopoDS_Shape>();
+    FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aPrs->object());
+    if (aFeature.get()) {
+      myStackWgt->setCurrentIndex(myFeaturePanelId);
+      buildFeaturePane(aFeature);
+    }
+    else {
+      myStackWgt->setCurrentIndex(myShapePanelId);
+      TopoDS_Shape aShape = ModuleBase_Tools::getSelectedShape(aPrs);
+      if (aShape.IsNull()) {
+        ResultPtr aRes = std::dynamic_pointer_cast<ModelAPI_Result>(aPrs->object());
+        if (aRes.get()) {
+          GeomShapePtr aShpPtr = aRes->shape();
+          if (aShpPtr.get()) {
+            aShape = aShpPtr->impl<TopoDS_Shape>();
+          }
         }
       }
+      if (aShape.IsNull())
+        return;
+      GeomShapePtr aShapePtr(new GeomAPI_Shape());
+      aShapePtr->setImpl(new TopoDS_Shape(aShape));
+
+      ModuleBase_ViewerPrsPtr aPrsCopy(new ModuleBase_ViewerPrs(aPrs->object(), aShapePtr));
+      setName(XGUI_Tools::generateName(aPrsCopy));
+      setShapeContent(aShape);
+      setShapeParams(aShape);
     }
-    if (aShape.IsNull())
-      return;
-    GeomShapePtr aShapePtr(new GeomAPI_Shape());
-    aShapePtr->setImpl(new TopoDS_Shape(aShape));
-
-    ModuleBase_ViewerPrsPtr aPrsCopy(new ModuleBase_ViewerPrs(aPrs->object(), aShapePtr));
-    setName(XGUI_Tools::generateName(aPrsCopy));
-    setShapeContent(aShape);
-    setShapeParams(aShape);
   }
 }
 
@@ -606,6 +641,7 @@ void XGUI_InspectionPanel::fillContainer(const GeomShapePtr& theShape)
   appendPointToParameters(tr("Maximal corner"), aMaxPoint, aParams);
 }
 
+//********************************************************************
 void XGUI_InspectionPanel::setPlaneType(const QString& theTitle,
                                         const std::shared_ptr<GeomAPI_Pln>& thePlane)
 {
@@ -616,6 +652,7 @@ void XGUI_InspectionPanel::setPlaneType(const QString& theTitle,
   setParamsText(aParams);
 }
 
+//********************************************************************
 void XGUI_InspectionPanel::setSphereType(const QString& theTitle,
                                          const std::shared_ptr<GeomAPI_Sphere>& theSphere)
 {
@@ -627,6 +664,7 @@ void XGUI_InspectionPanel::setSphereType(const QString& theTitle,
   setParamsText(aParams);
 }
 
+//********************************************************************
 void XGUI_InspectionPanel::setCylinderType(const QString& theTitle,
                                            const std::shared_ptr<GeomAPI_Cylinder>& theCyl)
 {
@@ -640,6 +678,7 @@ void XGUI_InspectionPanel::setCylinderType(const QString& theTitle,
   setParamsText(aParams);
 }
 
+//********************************************************************
 void XGUI_InspectionPanel::setConeType(const QString& theTitle,
                                        const std::shared_ptr<GeomAPI_Cone>& theCone)
 {
@@ -654,6 +693,7 @@ void XGUI_InspectionPanel::setConeType(const QString& theTitle,
   setParamsText(aParams);
 }
 
+//********************************************************************
 void XGUI_InspectionPanel::setTorusType(const QString& theTitle,
                                         const std::shared_ptr<GeomAPI_Torus>& theTorus)
 {
@@ -667,6 +707,7 @@ void XGUI_InspectionPanel::setTorusType(const QString& theTitle,
   setParamsText(aParams);
 }
 
+//********************************************************************
 void XGUI_InspectionPanel::setBoxType(const QString& theTitle,
                                       const std::shared_ptr<GeomAPI_Box>& theBox)
 {
@@ -680,6 +721,7 @@ void XGUI_InspectionPanel::setBoxType(const QString& theTitle,
   setParamsText(aParams);
 }
 
+//********************************************************************
 void XGUI_InspectionPanel::setRotatedBoxType(const QString& theTitle,
                                              const std::shared_ptr<GeomAPI_Box>& theBox)
 {
@@ -697,7 +739,43 @@ void XGUI_InspectionPanel::setRotatedBoxType(const QString& theTitle,
 }
 
 
+//********************************************************************
 void XGUI_InspectionPanel::setParamsText(const QString& theText)
 {
   myTypeParams->setText(theText);
 }
+
+//********************************************************************
+void XGUI_InspectionPanel::buildFeaturePane(const FeaturePtr& theFeature)
+{
+  std::string aXmlCfg, aDescription;
+  myWorkshop->module()->getXMLRepresentation(theFeature->getKind(), aXmlCfg, aDescription);
+  if (!aXmlCfg.empty()) {
+    QList<ModuleBase_ModelWidget*> aWidgets;
+    if (!myWorkshop->module()->createWidgets(theFeature, aXmlCfg.c_str(), aWidgets)) {
+      ModuleBase_WidgetFactory aFactory(aXmlCfg, myWorkshop->moduleConnector());
+      aFactory.createWidget(myFeaturePane);
+      aWidgets = aFactory.getModelWidgets();
+    }
+    foreach(ModuleBase_ModelWidget* aWgt, aWidgets) {
+      if (aWgt->isInformative()) {
+        aWgt->setEnabled(false);
+        aWgt->setFeature(theFeature, false, false);
+        aWgt->setEditingMode(true);
+        aWgt->restoreValue();
+        aWgt->showInformativePage();
+      }
+      else {
+        aWgt->setFeature(theFeature, false, false);
+        aWgt->setEditingMode(true);
+        aWgt->hide();
+      }
+    }
+  }
+}
+
+void XGUI_InspectionPanel::showEvent(QShowEvent* theEvent)
+{
+  QDockWidget::showEvent(theEvent);
+  onSelectionChanged();
+}
index 3f980ea42071aecdc69ee4e293b909f51ef6fdae..9f60ff56ea7291d00c797ce36d7c137bf4920399 100644 (file)
 
 #include "XGUI.h"
 
+#include <ModelAPI_Feature.h>
+
 #include <QDockWidget>
 
 #include <memory>
 
-class XGUI_SelectionMgr;
+class XGUI_Workshop;
 class QLineEdit;
 class QTableWidget;
 class QLabel;
 class QTextBrowser;
 class QVBoxLayout;
 class QResizeEvent;
+class QStackedWidget;
+class QGridLayout;
 
 class TopoDS_Shape;
 
@@ -50,6 +54,7 @@ class GeomAPI_Cylinder;
 class GeomAPI_Cone;
 class GeomAPI_Torus;
 class GeomAPI_Box;
+class ModuleBase_PageWidget;
 
 /// Internal name of property panel widget
 const static char* INSPECTION_PANEL = "inspection_panel_dock";
@@ -78,11 +83,13 @@ public:
   /// Constructor
   /// \param theParent is a parent of the property panel
   /// \param theMgr operation manager
-  XGUI_InspectionPanel(QWidget* theParent, XGUI_SelectionMgr* theMgr);
+  XGUI_InspectionPanel(QWidget* theParent, XGUI_Workshop* theWorkshop);
 
   // Destructor
   virtual ~XGUI_InspectionPanel();
 
+protected:
+  virtual void showEvent(QShowEvent* theEvent);
 
 private slots:
   /// A slot to react on selection changed
@@ -172,20 +179,27 @@ private:
   /// \param theBox the box
   void setRotatedBoxType(const QString& theTitle, const std::shared_ptr<GeomAPI_Box>& theBox);
 
-
   /// Set text into parameters area
   /// \param theText the text
   void setParamsText(const QString& theText);
 
+  /// Fills Feature panel with controls specific to the given feature
+  /// \param theFeature the selected feature
+  void buildFeaturePane(const FeaturePtr& theFeature);
+
 private:
-  XGUI_SelectionMgr* mySelectionMgr; //> selection manager
-
-  QLineEdit* myNameEdt; //> Name field
-  QTableWidget* mySubShapesTab; //> table of sub-shapes
-  QLabel* myTypeLbl; //> label of a type
-  QTextBrowser* myTypeParams; //> parameters area
-  QVBoxLayout* myMainLayout; //> main layout
-  //QWidget* myMainWidget;  //> main widget
+  XGUI_Workshop* myWorkshop; //> selection manager
+
+  QLineEdit* myNameEdt; ///> Name field
+  QTableWidget* mySubShapesTab; ///> table of sub-shapes
+  QLabel* myTypeLbl; ///> label of a type
+  QTextBrowser* myTypeParams; ///> parameters area
+  QVBoxLayout* myMainLayout; ///> main layout
+  ModuleBase_PageWidget* myFeaturePane; ///> Content of feature property panel
+  QGridLayout* myFeatureLayout; ///> Layout of feature panel
+  QStackedWidget* myStackWgt; ///> base widget of the panel
+  int myShapePanelId;
+  int myFeaturePanelId;
 };
 
 #endif
\ No newline at end of file
index 61bc814f47c7db8fdfb36ce8cc1bc2104faf7868..36866a91eda927474d490538e3d4e00d5d9bebdc 100644 (file)
@@ -232,4 +232,38 @@ void XGUI_ModuleConnector::applyCurrentSelectionModes(const AISObjectPtr& theAIS
 {
   Handle(AIS_InteractiveObject) anIO = theAIS->impl<Handle(AIS_InteractiveObject)>();
   myWorkshop->selectionActivate()->activate(anIO, false);
-}
\ No newline at end of file
+}
+
+
+void XGUI_ModuleConnector::undo()
+{
+  myWorkshop->onUndo();
+}
+
+void XGUI_ModuleConnector::setCancelEnabled(bool toEnable)
+{
+  XGUI_ActionsMgr* anActionsMgr = workshop()->actionsMgr();
+  QAction* aAbortAction = anActionsMgr->operationStateAction(XGUI_ActionsMgr::AbortAll);
+  QAction* aAbortAllAction = anActionsMgr->operationStateAction(XGUI_ActionsMgr::Abort);
+  if (aAbortAction) {
+    aAbortAction->setEnabled(toEnable);
+  }
+  if (aAbortAllAction) {
+    aAbortAllAction->setEnabled(toEnable);
+  }
+}
+
+bool XGUI_ModuleConnector::isCancelEnabled() const
+{
+  XGUI_ActionsMgr* anActionsMgr = workshop()->actionsMgr();
+  QAction* aAbortAction = anActionsMgr->operationStateAction(XGUI_ActionsMgr::AbortAll);
+  QAction* aAbortAllAction = anActionsMgr->operationStateAction(XGUI_ActionsMgr::Abort);
+  bool isEnabled = false;
+  if (aAbortAction) {
+    isEnabled = true;
+  }
+  if (aAbortAllAction) {
+    isEnabled &= true;
+  }
+  return isEnabled;
+}
index 41415fdf39be9daf4c70fd9d6eed77cd5c516dfb..ca31763a8c95b895eae38cc88635ed6e37173dfb 100644 (file)
@@ -136,6 +136,15 @@ Q_OBJECT
   //! \param theAIS the object which has to be activated
   virtual void applyCurrentSelectionModes(const AISObjectPtr& theAIS);
 
+  //! Undo last command
+  virtual void undo();
+
+  //! Set enabled state of cancel button in property panel
+  virtual void setCancelEnabled(bool toEnable);
+
+  //! Returns current state of cancel button
+  virtual bool isCancelEnabled() const;
+
 private:
   QObjectPtrList activeObjects(const QObjectPtrList& theObjList) const;
 
index f159dd4b5f4bc086dd509b611c3911148626e51b..b8857fecfe6ab87fc9f6abd40c19efa177afd71f 100644 (file)
@@ -628,8 +628,8 @@ void XGUI_OperationMgr::onOperationStopped()
     }
   }
   if (aResultOp) {
-    bool isModified = aCurrentOperation->isModified();
-    aResultOp->setIsModified(aResultOp->isModified() || isModified);
+    //bool isModified = aCurrentOperation->isModified();
+    //aResultOp->setIsModified(aResultOp->isModified() || isModified);
     resumeOperation(aResultOp);
     onValidateOperation();
   }
index 17d4f6d59b5f59f3289c0e3fd004a29de77fa5ae..b7c1a65bca80bb5cf842574e5010e8fdd1ba2125 100644 (file)
@@ -106,6 +106,7 @@ void XGUI_SelectionMgr::setSelectedOwners(const SelectMgr_IndexedMapOfOwner& the
 //**************************************************************
 void XGUI_SelectionMgr::onObjectBrowserSelection()
 {
+  myLastSelectionPlace = ModuleBase_ISelection::Browser;
   QList<ModuleBase_ViewerPrsPtr> aSelectedPrs =
     myWorkshop->selector()->selection()->getSelected(ModuleBase_ISelection::Browser);
   XGUI_Displayer* aDisplayer = myWorkshop->displayer();
@@ -138,6 +139,7 @@ void XGUI_SelectionMgr::onObjectBrowserSelection()
 //**************************************************************
 void XGUI_SelectionMgr::onViewerSelection()
 {
+  myLastSelectionPlace = ModuleBase_ISelection::Viewer;
   QList<ModuleBase_ViewerPrsPtr> aValues;
   Handle(AIS_InteractiveContext) aContext = myWorkshop->viewer()->AISContext();
   if (!aContext.IsNull())
index 1a4e519b3a2ad1a5e2a6323c724ba169b993a34b..6b7adae6b5361f65459c60fa0193211346d51628 100644 (file)
@@ -57,6 +57,10 @@ Q_OBJECT
     return mySelection;
   }
 
+  ModuleBase_ISelection::SelectionPlace placeOfSelection() const {
+    return myLastSelectionPlace;
+  }
+
   //! Connects the manager to all viewers accessible by Workshop
   void connectViewers();
 
@@ -111,6 +115,8 @@ private:
 
   /// Current selection object
   XGUI_Selection* mySelection;
+
+  ModuleBase_ISelection::SelectionPlace myLastSelectionPlace;
 };
 
 #endif
index cb66e1837fdaf24eb62da0d0f3ce818caf4f65e2..ae2bb898412f6d02e03a09813dec6e30b8ef9058 100644 (file)
@@ -250,40 +250,42 @@ QString generateName(const ModuleBase_ViewerPrsPtr& thePrs)
   if (aContext.get()) {
     GeomShapePtr aSubShape(new GeomAPI_Shape());
     TopoDS_Shape aShape = ModuleBase_Tools::getSelectedShape(thePrs);
-    aSubShape->setImpl(new TopoDS_Shape(aShape));
-    if (!aSubShape->isEqual(aContext)) {
-      QString aTypeName;
-      switch (aShape.ShapeType()) {
-      case TopAbs_COMPOUND:
-        aTypeName = "compound";
-        break;
-      case TopAbs_COMPSOLID:
-        aTypeName = "compsolid";
-        break;
-      case TopAbs_SOLID:
-        aTypeName = "solid";
-        break;
-      case TopAbs_SHELL:
-        aTypeName = "shell";
-        break;
-      case TopAbs_FACE:
-        aTypeName = "face";
-        break;
-      case TopAbs_WIRE:
-        aTypeName = "wire";
-        break;
-      case TopAbs_EDGE:
-        aTypeName = "edge";
-        break;
-      case TopAbs_VERTEX:
-        aTypeName = "vertex";
-        break;
-      case TopAbs_SHAPE:
-        aTypeName = "shape";
-        break;
+    if (!aShape.IsNull()) {
+      aSubShape->setImpl(new TopoDS_Shape(aShape));
+      if (!aSubShape->isEqual(aContext)) {
+        QString aTypeName;
+        switch (aShape.ShapeType()) {
+        case TopAbs_COMPOUND:
+          aTypeName = "compound";
+          break;
+        case TopAbs_COMPSOLID:
+          aTypeName = "compsolid";
+          break;
+        case TopAbs_SOLID:
+          aTypeName = "solid";
+          break;
+        case TopAbs_SHELL:
+          aTypeName = "shell";
+          break;
+        case TopAbs_FACE:
+          aTypeName = "face";
+          break;
+        case TopAbs_WIRE:
+          aTypeName = "wire";
+          break;
+        case TopAbs_EDGE:
+          aTypeName = "edge";
+          break;
+        case TopAbs_VERTEX:
+          aTypeName = "vertex";
+          break;
+        case TopAbs_SHAPE:
+          aTypeName = "shape";
+          break;
+        }
+        int aId = GeomAlgoAPI_CompoundBuilder::id(aContext, aSubShape);
+        aName += QString("/%1_%2").arg(aTypeName).arg(aId);
       }
-      int aId = GeomAlgoAPI_CompoundBuilder::id(aContext, aSubShape);
-      aName += QString("/%1_%2").arg(aTypeName).arg(aId);
     }
   }
   return aName;
index 28b7699ec29957bd6f281521c2e315fbff1d1cb3..2dd70bb5ab53d4c85dbedab16b6abb5881c3eeaf 100644 (file)
@@ -66,6 +66,7 @@
 #include <ModelAPI_AttributeDocRef.h>
 #include <ModelAPI_AttributeIntArray.h>
 #include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeString.h>
 #include <ModelAPI_Data.h>
 #include <ModelAPI_Events.h>
 #include <ModelAPI_Feature.h>
@@ -86,6 +87,9 @@
 #include <Events_InfoMessage.h>
 #include <Events_LongOp.h>
 
+#include <ExchangePlugin_ExportPart.h>
+#include <ExchangePlugin_ImportPart.h>
+
 #include <GeomAPI_Pnt.h>
 
 #include <ModuleBase_IModule.h>
 #include <ModuleBase_Tools.h>
 #include <ModuleBase_WidgetFactory.h>
 #include <ModuleBase_OperationFeature.h>
-#include <ModuleBase_OperationAction.h>
 #include <ModuleBase_PagedContainer.h>
 #include <ModuleBase_WidgetValidated.h>
 #include <ModuleBase_ModelWidget.h>
@@ -182,6 +185,8 @@ static QString MyFilter2(QObject::tr("CAD Builder files (*.cadbld)"));
 static QString MyExtension(".cadbld");
 #endif
 
+static QString MyImportPartFilter(QObject::tr("Part files (*.shaperpart);;All files (*.*)"));
+
 
 //******************************************************
 XGUI_Workshop::XGUI_Workshop(XGUI_SalomeConnector* theConnector)
@@ -273,6 +278,9 @@ XGUI_Workshop::XGUI_Workshop(XGUI_SalomeConnector* theConnector)
   connect(myEventsListener, SIGNAL(errorOccurred(std::shared_ptr<Events_InfoMessage>)),
           myErrorDlg, SLOT(addError(std::shared_ptr<Events_InfoMessage>)));
 
+  Config_PropManager::registerProp("Visualization", "selection_color", "Selection color",
+    Config_Prop::Color, "255,255,255");
+
   if (ModuleBase_Preferences::resourceMgr()->booleanValue("Viewer", "face-selection", true))
     myViewerSelMode.append(TopAbs_FACE);
   if (ModuleBase_Preferences::resourceMgr()->booleanValue("Viewer", "edge-selection", true))
@@ -329,6 +337,15 @@ void XGUI_Workshop::startApplication()
   // Calling of  loadCustomProps before activating module is required
   // by Config_PropManger to restore user-defined path to plugins
   ModuleBase_Preferences::loadCustomProps();
+  std::vector<int> aColor;
+  try {
+    aColor = Config_PropManager::color("Visualization", "selection_color");
+  }
+  catch (...) {
+  }
+  if (aColor.size() == 3)
+    myDisplayer->setSelectionColor(aColor);
+
   createModule();
 
 #ifndef HAVE_SALOME
@@ -453,6 +470,19 @@ void XGUI_Workshop::initMenu()
   connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onOpen()));
   salomeConnector()->addDesktopMenuSeparator("MEN_DESK_FILE");
 
+  aAction = salomeConnector()->addDesktopCommand("EXPORT_PART_CMD", tr("Export part..."),
+                                          tr("Export a part of the current document into a file"),
+                                          QIcon(), QKeySequence(),
+                                          false, "MEN_DESK_FILE");
+  connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onExportPart()));
+
+  aAction = salomeConnector()->addDesktopCommand("IMPORT_PART_CMD", tr("Import part..."),
+                                          tr("Import structure of a part"),
+                                          QIcon(), QKeySequence(),
+                                          false, "MEN_DESK_FILE");
+  connect(aAction, SIGNAL(triggered(bool)), this, SLOT(onImportPart()));
+  salomeConnector()->addDesktopMenuSeparator("MEN_DESK_FILE");
+
 #else
   // File commands group
   AppElements_MenuGroupPanel* aGroup = myMainWindow->menuObject()->generalPage();
@@ -659,12 +689,11 @@ void XGUI_Workshop::fillPropertyPanel(ModuleBase_Operation* theOperation)
   if (!aFOperation)
     return;
 
-  showPanel(myPropertyPanel);
   myPropertyPanel->cleanContent();
 
   QList<ModuleBase_ModelWidget*> aWidgets;
-  if (!module()->createWidgets(theOperation, aWidgets)) {
-    QString aXmlRepr = aFOperation->getDescription()->xmlRepresentation();
+  QString aXmlRepr = aFOperation->getDescription()->xmlRepresentation();
+  if (!module()->createWidgets(aFOperation->feature(), aXmlRepr, aWidgets)) {
     ModuleBase_WidgetFactory aFactory(aXmlRepr.toStdString(), myModuleConnector);
     aFactory.createWidget(myPropertyPanel->contentWidget());
     aWidgets = aFactory.getModelWidgets();
@@ -745,6 +774,10 @@ void XGUI_Workshop::fillPropertyPanel(ModuleBase_Operation* theOperation)
 #endif
 
   myErrorMgr->setPropertyPanel(myPropertyPanel);
+  theOperation->setHideFacesVisible(myFacesPanel->isVisible());
+  if (aFeatureInfo->isHideFacesPanel() && !myFacesPanel->isVisible())
+    myFacesPanel->show();
+  showPanel(myPropertyPanel);
 }
 
 //******************************************************
@@ -826,6 +859,9 @@ void XGUI_Workshop::onOperationStopped(ModuleBase_Operation* theOperation)
     }
   }
   activateObjectsSelection(anObjects);
+
+  if (!theOperation->isHideFacesVisible())
+    myFacesPanel->hide();
 }
 
 //******************************************************
@@ -1035,8 +1071,7 @@ void XGUI_Workshop::onPreferences()
   ModuleBase_Preferences::editPreferences(aModif);
   if (aModif.size() > 0) {
     QString aSection;
-    foreach (ModuleBase_Pref aPref, aModif)
-    {
+    foreach (ModuleBase_Pref aPref, aModif) {
       aSection = aPref.first;
       if (aSection == ModuleBase_Preferences::VIEWER_SECTION) {
         myMainWindow->viewer()->updateFromResources();
@@ -1044,6 +1079,15 @@ void XGUI_Workshop::onPreferences()
         myMainWindow->menuObject()->updateFromResources();
       }
     }
+    std::vector<int> aColor;
+    try {
+      aColor = Config_PropManager::color("Visualization", "selection_color");
+    }
+    catch (...) {
+    }
+    if (aColor.size() == 3)
+      displayer()->setSelectionColor(aColor);
+
     displayer()->redisplayObjects();
   }
 }
@@ -1226,6 +1270,42 @@ void XGUI_Workshop::onWidgetObjectUpdated()
   myDisplayer->updateViewer();
 }
 
+//******************************************************
+void XGUI_Workshop::onImportPart()
+{
+  if (!abortAllOperations())
+    return;
+
+  //show file dialog, check if readable and open
+  qreal aRatio = ModuleBase_Tools::currentPixelRatio();
+  // If the ratio is > 1 (HD screen) then QT has a bug in
+  // displaying of system open file dialog (too small)
+  QString aFile = QFileDialog::getOpenFileName(desktop(), tr("Import part"), QString(),
+        MyImportPartFilter, Q_NULLPTR,
+        ((aRatio > 1) ? QFileDialog::DontUseNativeDialog : QFileDialog::Options()));
+  if (!aFile.isNull()) {
+    ModuleBase_OperationFeature* anImportPartOp = dynamic_cast<ModuleBase_OperationFeature*>(
+        module()->createOperation(ExchangePlugin_ImportPart::ID()));
+    if (operationMgr()->startOperation(anImportPartOp)) {
+      // initialize the filename to be imported
+      FeaturePtr aFeature = anImportPartOp->feature();
+      aFeature->string(ExchangePlugin_ImportPart::FILE_PATH_ID())->setValue(aFile.toStdString());
+      ModuleBase_Tools::flushUpdated(aFeature);
+      operationMgr()->commitOperation();
+    }
+  }
+}
+
+//******************************************************
+void XGUI_Workshop::onExportPart()
+{
+  if (abortAllOperations()) {
+    ModuleBase_OperationFeature* anExportPartOp = dynamic_cast<ModuleBase_OperationFeature*>(
+        module()->createOperation(ExchangePlugin_ExportPart::ID()));
+    operationMgr()->startOperation(anExportPartOp);
+  }
+}
+
 //******************************************************
 ModuleBase_IModule* XGUI_Workshop::loadModule(const QString& theModule)
 {
@@ -1426,14 +1506,14 @@ void XGUI_Workshop::createDockWidgets()
 
   hidePanel(myPropertyPanel);  ///<! Invisible by default
 
-  myFacesPanel = new XGUI_FacesPanel(aDesktop, myModuleConnector);
+  myFacesPanel = new XGUI_FacesPanel(aDesktop, this);
   myActiveControlMgr->addSelector(new XGUI_FacesPanelSelector(myFacesPanel));
   myFacesPanel->setAllowedAreas(Qt::LeftDockWidgetArea |
                                 Qt::RightDockWidgetArea |
                                 Qt::BottomDockWidgetArea);
   connect(myFacesPanel, SIGNAL(closed()), myFacesPanel, SLOT(onClosed()));
 
-  myInspectionPanel = new XGUI_InspectionPanel(aDesktop, mySelector);
+  myInspectionPanel = new XGUI_InspectionPanel(aDesktop, this);
   myInspectionPanel->setAllowedAreas(Qt::LeftDockWidgetArea |
     Qt::RightDockWidgetArea);
   aDesktop->addDockWidget(Qt::RightDockWidgetArea, myInspectionPanel);
@@ -1826,7 +1906,7 @@ void XGUI_Workshop::deleteObjects()
   bool aDone = false;
   QString aDescription = contextMenuMgr()->action("DELETE_CMD")->text() + " %1";
   aDescription = aDescription.arg(XGUI_Tools::unionOfObjectNames(anObjects, ", "));
-  ModuleBase_OperationAction* anOpAction = new ModuleBase_OperationAction(aDescription, module());
+  ModuleBase_Operation* anOpAction = new ModuleBase_Operation(aDescription, module());
 
   operationMgr()->startOperation(anOpAction);
 
@@ -1983,7 +2063,7 @@ void XGUI_Workshop::cleanHistory()
     // 1. start operation
     aDescription += "by deleting of " +
       aDescription.arg(XGUI_Tools::unionOfObjectNames(anObjects, ", "));
-    ModuleBase_OperationAction* anOpAction = new ModuleBase_OperationAction(aDescription, module());
+    ModuleBase_Operation* anOpAction = new ModuleBase_Operation(aDescription, module());
     operationMgr()->startOperation(anOpAction);
 
     // WORKAROUND, should be done before each object remove, if it presents in XGUI_DataModel tree
index 8f524f3cb5d92244bc573f7356c612d00d989f52..4b06dac3f54ce95e4804a063b59957d5e081708c 100644 (file)
@@ -395,6 +395,12 @@ signals:
   /// Create a new document
   void onNew();
 
+  /// Import part structure from a file
+  void onImportPart();
+
+  /// Export features to a file
+  void onExportPart();
+
 #ifndef HAVE_SALOME
   /// Exit application
   void onExit();
diff --git a/test.sh b/test.sh
index 92c0b48e9651a0b4d16e12a6f178499fbe08164a..7faf456b873ec3f33bcbb5ba2aafa9d7ed94797d 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -7,6 +7,13 @@ cd ${BUILD_DIR}
 
 export DISPLAY="localhost:0.0"
 
+# check for __init__.py in the SHAPER's Python scripts directory
+# for correct parsing PYTHONPATH and use the actual version of SHAPER
+# instead of distributed with SALOME
+if [[ ! -f ${SHAPER_PYTHON_SCRIPTS_DIR}/salome/__init__.py ]]; then
+  touch ${SHAPER_PYTHON_SCRIPTS_DIR}/salome/__init__.py
+fi
+
 if [[ $# > 0 ]]; then
   ctest --no-compress-output -T Test "$@" -R $1
 else