Salome HOME
Merge branch 'occ/shaper2smesh'
authorvsr <vsr@opencascade.com>
Fri, 21 Feb 2020 12:22:23 +0000 (15:22 +0300)
committervsr <vsr@opencascade.com>
Fri, 21 Feb 2020 12:22:23 +0000 (15:22 +0300)
353 files changed:
doc/gui/Introduction.rst
doc/gui/images/hide_faces_panel.png [new file with mode: 0644]
doc/gui/images/sketch_preferences.png
env.sh
lcov_reports.sh
salomeRun_deb.bat
salomeRun_rel.bat
src/BuildPlugin/CMakeLists.txt
src/BuildPlugin/Test/Test3125.py [new file with mode: 0644]
src/BuildPlugin/doc/faceFeature.rst
src/BuildPlugin/doc/images/Edge.png
src/BuildPlugin/doc/images/Wire.png
src/BuildPlugin/doc/vertexFeature.rst
src/BuildPlugin/wire_widget.xml
src/CollectionPlugin/CMakeLists.txt
src/CollectionPlugin/CollectionPlugin_Group.cpp
src/CollectionPlugin/Test/Test18739.py [new file with mode: 0644]
src/CollectionPlugin/Test/Test3114.py [new file with mode: 0644]
src/Config/Config_Prop.h
src/ConstructionPlugin/ConstructionPlugin_Plugin.cpp
src/ConstructionPlugin/doc/images/Point2.png
src/ConstructionPlugin/doc/images/Point3-1.png [new file with mode: 0644]
src/ConstructionPlugin/doc/images/Point3.png
src/ConstructionPlugin/doc/images/Point4-1.png [new file with mode: 0644]
src/ConstructionPlugin/doc/images/Point4-2.png [new file with mode: 0644]
src/ConstructionPlugin/doc/images/Point4.png
src/ConstructionPlugin/doc/images/Point5-1.png [new file with mode: 0644]
src/ConstructionPlugin/doc/images/Point5.png
src/ConstructionPlugin/doc/pointFeature.rst
src/ExchangeAPI/ExchangeAPI_Export.cpp
src/ExchangePlugin/CMakeLists.txt
src/ExchangePlugin/ExchangePlugin_Dump.cpp
src/ExchangePlugin/ExchangePlugin_ExportFeature.cpp
src/ExchangePlugin/ExchangePlugin_ExportFeature.h
src/ExchangePlugin/ExchangePlugin_ExportPart.cpp
src/ExchangePlugin/ExchangePlugin_msg_en.ts
src/ExchangePlugin/ExchangePlugin_msg_fr.ts
src/ExchangePlugin/Test/Test18710.py [new file with mode: 0644]
src/FeaturesAPI/CMakeLists.txt
src/FeaturesAPI/FeaturesAPI.i
src/FeaturesAPI/FeaturesAPI_Copy.cpp [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_Copy.h [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_Defeaturing.cpp [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_Defeaturing.h [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_ImportResult.cpp [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_ImportResult.h [new file with mode: 0644]
src/FeaturesAPI/FeaturesAPI_swig.h
src/FeaturesPlugin/CMakeLists.txt
src/FeaturesPlugin/FeaturesPlugin_Copy.cpp [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Copy.h [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Defeaturing.cpp [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Defeaturing.h [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_ImportResult.cpp [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_ImportResult.h [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_MultiRotation.cpp
src/FeaturesPlugin/FeaturesPlugin_MultiTranslation.cpp
src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp
src/FeaturesPlugin/FeaturesPlugin_Validators.cpp
src/FeaturesPlugin/FeaturesPlugin_Validators.h
src/FeaturesPlugin/FeaturesPlugin_msg_fr.ts
src/FeaturesPlugin/FeaturesPlugin_msg_ru.ts
src/FeaturesPlugin/Test/Test2918.py [new file with mode: 0644]
src/FeaturesPlugin/Test/Test3137_1.py [new file with mode: 0644]
src/FeaturesPlugin/Test/Test3137_2.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyFeature.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyFeatureMoveGroupOfFeature.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyMoveResult.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyMoveSubShapes.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyNames.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopySubShapes.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestCopyWholeFeature.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_ErrorMsg.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_OnCompound.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid1.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid2.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid3.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_OnSolid1.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_OnSolid2.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestDefeaturing_OnSolid3.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestImportResult.py [new file with mode: 0644]
src/FeaturesPlugin/Test/TestMeasurementAngle3Points.py
src/FeaturesPlugin/copy_widget.xml [new file with mode: 0644]
src/FeaturesPlugin/defeaturing_widget.xml [new file with mode: 0644]
src/FeaturesPlugin/doc/FeaturesPlugin.rst
src/FeaturesPlugin/doc/TUI_copyFeature.rst [new file with mode: 0644]
src/FeaturesPlugin/doc/TUI_defeaturingFeature.rst [new file with mode: 0644]
src/FeaturesPlugin/doc/TUI_importResultFeature.rst [new file with mode: 0644]
src/FeaturesPlugin/doc/copyFeature.rst [new file with mode: 0644]
src/FeaturesPlugin/doc/defeaturingFeature.rst [new file with mode: 0644]
src/FeaturesPlugin/doc/examples/copy.py [new file with mode: 0644]
src/FeaturesPlugin/doc/examples/defeaturing.py [new file with mode: 0644]
src/FeaturesPlugin/doc/examples/import_result.py [new file with mode: 0644]
src/FeaturesPlugin/doc/images/Copy.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/CreatedCopy.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/CreatedImportResult.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/ImportResult.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/copy_btn.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/defeaturing.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/defeaturing_property_panel.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/defeaturing_result.png [new file with mode: 0644]
src/FeaturesPlugin/doc/images/import_result_btn.png [new file with mode: 0644]
src/FeaturesPlugin/doc/importResultFeature.rst [new file with mode: 0644]
src/FeaturesPlugin/doc/unionFeature.rst
src/FeaturesPlugin/icons/copy.png [new file with mode: 0644]
src/FeaturesPlugin/icons/defeaturing.png [new file with mode: 0644]
src/FeaturesPlugin/icons/import_result.png [new file with mode: 0644]
src/FeaturesPlugin/import_result_widget.xml [new file with mode: 0644]
src/FeaturesPlugin/plugin-Features.xml
src/GeomAPI/CMakeLists.txt
src/GeomAPI/GeomAPI.i
src/GeomAPI/GeomAPI_BSpline.cpp [new file with mode: 0644]
src/GeomAPI/GeomAPI_BSpline.h [new file with mode: 0644]
src/GeomAPI/GeomAPI_BSpline2d.cpp [new file with mode: 0644]
src/GeomAPI/GeomAPI_BSpline2d.h [new file with mode: 0644]
src/GeomAPI/GeomAPI_Edge.cpp
src/GeomAPI/GeomAPI_Edge.h
src/GeomAPI/GeomAPI_Shape.cpp
src/GeomAPI/GeomAPI_Shape.h
src/GeomAPI/GeomAPI_swig.h
src/GeomAlgoAPI/CMakeLists.txt
src/GeomAlgoAPI/GeomAlgoAPI_Defeaturing.cpp [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI_Defeaturing.h [new file with mode: 0644]
src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.cpp
src/GeomAlgoAPI/GeomAlgoAPI_EdgeBuilder.h
src/GeomAlgoAPI/GeomAlgoAPI_UnifySameDomain.cpp
src/GeomData/CMakeLists.txt
src/GeomData/GeomData_Point2DArray.cpp [new file with mode: 0644]
src/GeomData/GeomData_Point2DArray.h [new file with mode: 0644]
src/GeomDataAPI/CMakeLists.txt
src/GeomDataAPI/GeomDataAPI.i
src/GeomDataAPI/GeomDataAPI_Point2DArray.cpp [new file with mode: 0644]
src/GeomDataAPI/GeomDataAPI_Point2DArray.h [new file with mode: 0644]
src/GeomDataAPI/GeomDataAPI_swig.h
src/Model/Model_AttributeRefAttrList.cpp
src/Model/Model_AttributeReference.cpp
src/Model/Model_AttributeSelection.cpp
src/Model/Model_AttributeSelection.h
src/Model/Model_AttributeSelectionList.cpp
src/Model/Model_AttributeSelectionList.h
src/Model/Model_Data.cpp
src/Model/Model_Document.cpp
src/Model/Model_Objects.cpp
src/Model/Model_ResultBody.cpp
src/Model/Model_ResultBody.h
src/Model/Model_ResultPart.cpp
src/Model/Model_ResultPart.h
src/Model/Model_Update.cpp
src/ModelAPI/CMakeLists.txt
src/ModelAPI/ModelAPI.i
src/ModelAPI/ModelAPI_AttributeSelectionList.h
src/ModelAPI/ModelAPI_Events.cpp
src/ModelAPI/ModelAPI_Events.h
src/ModelAPI/ModelAPI_Feature.h
src/ModelAPI/ModelAPI_ResultBody.cpp
src/ModelAPI/ModelAPI_ResultBody.h
src/ModelAPI/ModelAPI_ResultPart.h
src/ModelAPI/ModelAPI_Tools.cpp
src/ModelAPI/ModelAPI_swig.h
src/ModelAPI/Test/Test3116.py [new file with mode: 0644]
src/ModelHighAPI/CMakeLists.txt
src/ModelHighAPI/ModelHighAPI.i
src/ModelHighAPI/ModelHighAPI_Double.cpp
src/ModelHighAPI/ModelHighAPI_Double.h
src/ModelHighAPI/ModelHighAPI_Dumper.cpp
src/ModelHighAPI/ModelHighAPI_Dumper.h
src/ModelHighAPI/ModelHighAPI_FeatureStore.cpp
src/ModelHighAPI/ModelHighAPI_RefAttr.cpp
src/ModelHighAPI/ModelHighAPI_Tools.cpp
src/ModelHighAPI/ModelHighAPI_Tools.h
src/ModelHighAPI/Test/Test18451.py [new file with mode: 0644]
src/ModuleBase/ModuleBase_IWidgetCreator.cpp
src/ModuleBase/ModuleBase_IWidgetCreator.h
src/ModuleBase/ModuleBase_ModelWidget.cpp
src/ModuleBase/ModuleBase_ModelWidget.h
src/ModuleBase/ModuleBase_Preferences.cpp
src/ModuleBase/ModuleBase_ResultPrs.cpp
src/ModuleBase/ModuleBase_ViewerFilters.cpp
src/ModuleBase/ModuleBase_WidgetCreatorFactory.cpp
src/ModuleBase/ModuleBase_WidgetCreatorFactory.h
src/ModuleBase/ModuleBase_WidgetFactory.cpp
src/ModuleBase/ModuleBase_WidgetMultiSelector.cpp
src/ModuleBase/ModuleBase_WidgetPointInput.cpp
src/ModuleBase/ModuleBase_msg_fr.ts
src/PartSet/CMakeLists.txt
src/PartSet/PartSet_BSplineWidget.cpp [new file with mode: 0644]
src/PartSet/PartSet_BSplineWidget.h [new file with mode: 0644]
src/PartSet/PartSet_MenuMgr.cpp
src/PartSet/PartSet_MenuMgr.h
src/PartSet/PartSet_Module.cpp
src/PartSet/PartSet_SketcherMgr.cpp
src/PartSet/PartSet_SketcherMgr.h
src/PartSet/PartSet_SketcherReentrantMgr.cpp
src/PartSet/PartSet_Tools.cpp
src/PartSet/PartSet_Tools.h
src/PartSet/PartSet_WidgetBSplinePoints.cpp [new file with mode: 0644]
src/PartSet/PartSet_WidgetBSplinePoints.h [new file with mode: 0644]
src/PartSet/PartSet_WidgetFeaturePointSelector.cpp
src/PartSet/PartSet_WidgetFeaturePointSelector.h
src/PartSet/PartSet_WidgetPoint2d.cpp
src/PartSet/PartSet_WidgetPoint2d.h
src/PartSet/PartSet_WidgetSketchLabel.cpp
src/PartSet/PartSet_msg_fr.ts
src/PythonAPI/model/features/__init__.py
src/SHAPERGUI/SHAPERGUI.cpp
src/SHAPERGUI/SHAPERGUI_SalomeViewer.cpp
src/SHAPERGUI/SHAPERGUI_msg_fr.ts
src/Selector/Selector_Modify.cpp
src/SketchAPI/CMakeLists.txt
src/SketchAPI/SketchAPI.i
src/SketchAPI/SketchAPI_BSpline.cpp [new file with mode: 0644]
src/SketchAPI/SketchAPI_BSpline.h [new file with mode: 0644]
src/SketchAPI/SketchAPI_Constraint.cpp
src/SketchAPI/SketchAPI_ConstraintAngle.cpp
src/SketchAPI/SketchAPI_Projection.cpp
src/SketchAPI/SketchAPI_Sketch.cpp
src/SketchAPI/SketchAPI_Sketch.h
src/SketchAPI/SketchAPI_SketchEntity.cpp
src/SketchAPI/SketchAPI_swig.h
src/SketchPlugin/CMakeLists.txt
src/SketchPlugin/SketchPlugin_BSpline.cpp [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_BSpline.h [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_BSplineBase.cpp [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_BSplineBase.h [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_BSplinePeriodic.cpp [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_BSplinePeriodic.h [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_ConstraintAngle.cpp
src/SketchPlugin/SketchPlugin_ConstraintAngle.h
src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.cpp
src/SketchPlugin/SketchPlugin_ConstraintCoincidenceInternal.h
src/SketchPlugin/SketchPlugin_MacroBSpline.cpp [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_MacroBSpline.h [new file with mode: 0644]
src/SketchPlugin/SketchPlugin_MacroEllipse.cpp
src/SketchPlugin/SketchPlugin_MacroEllipticArc.cpp
src/SketchPlugin/SketchPlugin_Plugin.cpp
src/SketchPlugin/SketchPlugin_Projection.cpp
src/SketchPlugin/SketchPlugin_Projection.h
src/SketchPlugin/SketchPlugin_Split.cpp
src/SketchPlugin/SketchPlugin_Tools.cpp
src/SketchPlugin/SketchPlugin_Tools.h
src/SketchPlugin/SketchPlugin_Trim.cpp
src/SketchPlugin/SketchPlugin_Validators.cpp
src/SketchPlugin/SketchPlugin_Validators.h
src/SketchPlugin/SketchPlugin_msg_fr.ts
src/SketchPlugin/Test/Test3087_1.py
src/SketchPlugin/Test/Test3087_2.py
src/SketchPlugin/Test/Test3132.py [new file with mode: 0644]
src/SketchPlugin/Test/TestArcBehavior.py
src/SketchPlugin/Test/TestBSplineAddPole.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngleBehaviorBackward_1.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngleBehaviorBackward_2.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngleBehaviorDirect.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngleBehaviorSupplementary_1.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngleBehaviorSupplementary_2.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngle_v0_1.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngle_v0_2.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngle_v20191210_1.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintAngle_v20191210_2.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintCoincidenceBSpline.py [new file with mode: 0644]
src/SketchPlugin/Test/TestConstraintMiddlePointOnEllipticArc.py
src/SketchPlugin/Test/TestConstraintTangentBSpline.py [new file with mode: 0644]
src/SketchPlugin/Test/TestCreateBSpline.py [new file with mode: 0644]
src/SketchPlugin/Test/TestCreateBSplinePeriodic.py [new file with mode: 0644]
src/SketchPlugin/Test/TestCreateMacroBSpline.py [new file with mode: 0644]
src/SketchPlugin/Test/TestMoveBSpline.py [new file with mode: 0644]
src/SketchPlugin/Test/TestMoveBSplinePeriodic.py [new file with mode: 0644]
src/SketchPlugin/Test/TestPresentation.py
src/SketchPlugin/Test/TestProjectionBSpline.py [new file with mode: 0644]
src/SketchPlugin/Test/TestProjectionBSplinePeriodic.py [new file with mode: 0644]
src/SketchPlugin/Test/TestRemoveBSpline.py [new file with mode: 0644]
src/SketchPlugin/Test/TestRemoveBSplinePeriodic.py [new file with mode: 0644]
src/SketchPlugin/doc/SketchPlugin.rst
src/SketchPlugin/doc/TUI_bsplineFeature.rst [new file with mode: 0644]
src/SketchPlugin/doc/bsplineFeature.rst [new file with mode: 0644]
src/SketchPlugin/doc/examples/bspline.py [new file with mode: 0644]
src/SketchPlugin/doc/images/bspline.png [new file with mode: 0644]
src/SketchPlugin/doc/images/bspline_add_pole.png [new file with mode: 0644]
src/SketchPlugin/doc/images/bspline_creation_panel.png [new file with mode: 0644]
src/SketchPlugin/doc/images/bspline_modification_panel.png [new file with mode: 0644]
src/SketchPlugin/doc/images/bspline_p.png [new file with mode: 0644]
src/SketchPlugin/doc/images/bspline_periodic_result.png [new file with mode: 0644]
src/SketchPlugin/doc/images/bspline_result.png [new file with mode: 0644]
src/SketchPlugin/icons/bspline.png [new file with mode: 0644]
src/SketchPlugin/icons/bspline_p.png [new file with mode: 0644]
src/SketchPlugin/plugin-Sketch.xml
src/SketchSolver/PlaneGCSSolver/CMakeLists.txt
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_AttributeBuilder.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_BooleanWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ConstraintWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Defs.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EdgeWrapper.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_EntityWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_FeatureBuilder.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.cpp [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.h [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.cpp [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.h [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointWrapper.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.cpp [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.h [new file with mode: 0644]
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarWrapper.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarWrapper.h
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Storage.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.cpp
src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_Tools.h
src/SketchSolver/SketchSolver_Constraint.cpp
src/SketchSolver/SketchSolver_ConstraintCoincidence.cpp
src/SketchSolver/SketchSolver_ConstraintCoincidence.h
src/SketchSolver/SketchSolver_ConstraintFixed.cpp
src/SketchSolver/SketchSolver_ConstraintMovement.cpp
src/SketchSolver/SketchSolver_ConstraintMovement.h
src/SketchSolver/SketchSolver_ConstraintTangent.cpp
src/SketchSolver/SketchSolver_ConstraintTangent.h
src/SketchSolver/SketchSolver_Group.cpp
src/SketchSolver/SketchSolver_Group.h
src/SketchSolver/SketchSolver_Manager.cpp
src/SketchSolver/SketchSolver_Manager.h
src/SketcherPrs/CMakeLists.txt
src/SketcherPrs/SketcherPrs_Angle.cpp
src/SketcherPrs/SketcherPrs_Angle.h
src/SketcherPrs/SketcherPrs_DimensionStyle.cpp [new file with mode: 0644]
src/SketcherPrs/SketcherPrs_DimensionStyle.h [new file with mode: 0644]
src/SketcherPrs/SketcherPrs_DimensionStyleListener.cpp [deleted file]
src/SketcherPrs/SketcherPrs_DimensionStyleListener.h [deleted file]
src/SketcherPrs/SketcherPrs_LengthDimension.cpp
src/SketcherPrs/SketcherPrs_LengthDimension.h
src/SketcherPrs/SketcherPrs_Radius.cpp
src/SketcherPrs/SketcherPrs_Radius.h
src/SketcherPrs/SketcherPrs_SensitivePoint.cpp
src/SketcherPrs/SketcherPrs_SensitivePoint.h
src/SketcherPrs/SketcherPrs_SymbolPrs.cpp
src/SketcherPrs/SketcherPrs_Tools.cpp
src/SketcherPrs/SketcherPrs_Tools.h
src/XGUI/XGUI_Displayer.cpp
src/XGUI/XGUI_FacesPanel.cpp
src/XGUI/XGUI_FacesPanel.h
src/XGUI/XGUI_InspectionPanel.cpp
src/XGUI/XGUI_OperationMgr.cpp
src/XGUI/XGUI_PropertyPanel.cpp
src/XGUI/XGUI_SelectionMgr.cpp
src/XGUI/XGUI_ViewerProxy.cpp
src/XGUI/XGUI_Workshop.cpp
src/XGUI/XGUI_msg_fr.ts
src/XGUI/XGUI_pictures.qrc
src/XGUI/pictures/ArrowCursor.png [new file with mode: 0644]
src/XGUI/pictures/CrossCursor.png [new file with mode: 0644]
src/XGUI/pictures/HandCursor.png [new file with mode: 0644]
test.hdfs/CMakeLists.txt
test.hdfs/CuveN4_Tubulures.py [new file with mode: 0644]
test.hdfs/Test3061.hdf [new file with mode: 0644]
test.hdfs/Test3061.py [new file with mode: 0644]

index c35c806f64609bfa65a0e587bdb6d1e64a8e1fd1..0e493c4e7cd4ac6cb2343df93d68a5bb129c7ef8 100644 (file)
@@ -143,6 +143,7 @@ Standard dock windows are:
 \r
 - :ref:`object_browser`\r
 - :ref:`inspection_panel`\r
+- :ref:`hidefaces_panel`\r
 - :ref:`python console`\r
 - :ref:`property_panel`\r
 \r
@@ -354,6 +355,28 @@ The information about  Plane, Face additionally shows coordinates of center poin
 .. centered::\r
    Inspection panel for Face \r
 \r
+.. _hidefaces_panel:\r
+\r
+Hide Faces panel\r
+^^^^^^^^^^^^^^^^\r
+\r
+**Hide Faces** panel makes possible to hide temporary faces of any displayed object. **Hide Faces** panel looks like following:\r
+\r
+.. image:: images/hide_faces_panel.png\r
+   :align: center\r
+\r
+.. centered::\r
+   Hide Faces panel\r
+\r
+- If this panel is activated it "listens" user selection.\r
+- If a face is selected then its name will be shown in the panel's list and hidden in the viewer. \r
+- If user selects a group of faces (or at least a one face of this group) then whole group will be hidden and also all faces from all objects referenced by this group.\r
+- If user will display the hidden group again (by a show operation) then the group will be removed from Hide Faces list and visibility of all referenced faces will be restored.\r
+\r
+Also it is possible do not to hide faces, but make them transparent. For this purpose **"Transparent"** check-box can be used. Value of the transparency can be changed in **Visualization** tab of **Preferences** dialog box.\r
+Closing of **Hide Faces** panel restores visibility state of all objects. If it is necessary to deactivete the **Hide Faces** panel (preserving the current display state) then user has to press **"Esc"** button.\r
+\r
+\r
 .. _python console:\r
 \r
 Python console\r
@@ -602,6 +625,9 @@ Sketch tab defines properties of coordinate planes shown for selection of sketch
 - **Thickness**  defines thickness of coordinate plane borders; \r
 - **Rotate to plane when selected** check-box turns on/off automatic switch the viewer to the top view for the selected sketch plane.  \r
 - **Angular tolerance** defines defines an angular tolerance for automatic creation of horizontal and vertical constraints;\r
+- **Default spline weight** defines default weight for B-spline nodes during creation. The default value can be changed by editing of the spline;\r
+- **Cursor for sketch operation** defines a cursor which indicates a launched sketcher sub-operation.\r
+- **Create sketch entities by dragging** defines a style of sketche etities creation. It concerns creation of lines, rectangles, circles, arcs, ellipses, elliptic arcs. If it is switched ON then points of objects has to be defined by mouse press - mouse move - mouse rellease. Otherwise every point of an object has to be defined by mouse click;\r
 \r
    \r
 .. _viewer_preferences:\r
diff --git a/doc/gui/images/hide_faces_panel.png b/doc/gui/images/hide_faces_panel.png
new file mode 100644 (file)
index 0000000..c0a2e70
Binary files /dev/null and b/doc/gui/images/hide_faces_panel.png differ
index ce3b32ff2ff899e4128193a992f9a261561e1412..97044453421fc016cba30a4b6c4f5eef973b6563 100755 (executable)
Binary files a/doc/gui/images/sketch_preferences.png and b/doc/gui/images/sketch_preferences.png differ
diff --git a/env.sh b/env.sh
index cf9a4e0036506197521fa654326715fb3d887f8b..1b7c6dadab916a1003e722085627c844e3be2174 100644 (file)
--- a/env.sh
+++ b/env.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -x
 
-export SALOME_DIR=/dn46/SALOME/series9x/current-2019-10-10
+export SALOME_DIR=/dn46/SALOME/series9x/current-2020-02-03
 
 # Path to sources
 export SOURCES_DIR=$(pwd)
index cd094cf1528fb31670e24af8f0b45dfd6e2d4ce3..e2b48e3cade1f7c88ffacb85eeb6b63239075f04 100755 (executable)
@@ -9,8 +9,7 @@ lcov --capture --directory ${BUILD_DIR} --no-external --base-directory ${SOURCES
 # make a working copy of report
 cp -f coverage.info.noext covfile
 # remove all reports of GUI and external parts (for all the next kinds of reports)
-# RefAttrList is unused type of attribute for now
-for MASK in '*wrap*' 'moc_*' 'XAO_*' 'SketcherPrs_*' 'GeomAlgoImpl_*' 'ModuleBase_*' '*Widget*' '*Splitter*' '*RefAttrList*'; do
+for MASK in '*wrap*' 'moc_*' 'XAO_*' 'SketcherPrs_*' 'GeomAlgoImpl_*' 'ModuleBase_*' '*Widget*' '*Splitter*'; do
 lcov -r covfile ${MASK} --output-file covfile_res -q
 mv -f covfile_res covfile
 done
index be74b98121d9ac7844ebc337dc4d3e180d0c854f..5534c345ce059291fb2514d36701ddbd1658f51e 100644 (file)
@@ -10,6 +10,9 @@ popd
 @SET OCC_LIB_PREFIX=d
 call env_Salome.bat d run
 
+rem Variable which is necessary for launching SALOME
+SET SALOME_PLEASE_SETUP_ENVIRONMENT_AS_BEFORE=1
+
 @SET SHAPER_ROOT_DIR=%ROOT_DIR%\install
 @SET SalomeAppConfig=%SHAPER_ROOT_DIR%\share\salome\resources\shaper;%GUI_ROOT_DIR%\share\salome\resources\gui
 
index 2cfabdae9b283e2d0da722f4532eb33ff314cb79..82717fdb44e9a7bb029e15bba760e2c64d15713a 100644 (file)
@@ -9,6 +9,9 @@ popd
 
 call env_Salome.bat
 
+rem Variable which is necessary for launching SALOME
+SET SALOME_PLEASE_SETUP_ENVIRONMENT_AS_BEFORE=1
+
 @SET SHAPER_ROOT_DIR=%ROOT_DIR%\install
 @SET SalomeAppConfig=%SHAPER_ROOT_DIR%\share\salome\resources\shaper;%GUI_ROOT_DIR%\share\salome\resources\gui
 
index 2954b05c7f0cb94b7601731bcbd92103982f4172..d0568077dddc2fa021376588a2eb17e431f6c813 100644 (file)
@@ -155,4 +155,5 @@ ADD_UNIT_TESTS(TestVertex.py
                Test2415.py
                Test2439.py
                Test2454.py
+               Test3125.py
 )
diff --git a/src/BuildPlugin/Test/Test3125.py b/src/BuildPlugin/Test/Test3125.py
new file mode 100644 (file)
index 0000000..e592da0
--- /dev/null
@@ -0,0 +1,48 @@
+# 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
+#
+
+# Checking of adding a partset-sketcher edges into the wire by the "Add contour" action
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOZ"))
+SketchLine_1 = Sketch_1.addLine(-24.8768472906404, 33.33497536945814, 36.81773399014779, 23.38423645320196)
+SketchLine_2 = Sketch_1.addLine(36.81773399014779, 23.38423645320196, 11.31896551724138, -27.61330049261085)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(11.31896551724138, -27.61330049261085, -24.8768472906404, 33.33497536945814)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+model.do()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Wire_1_objects = [model.selection("EDGE", "PartSet/Sketch_1/SketchLine_3")]
+Wire_1 = model.addWire(Part_1_doc, Wire_1_objects, False)
+Wire_1.feature().customAction("add_contour")
+model.end()
+
+from ModelAPI import *
+from GeomAPI import *
+
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Wire_1.feature()))
+
+model.testNbResults(Wire_1, 1)
+model.testNbSubShapes(Wire_1, GeomAPI_Shape.EDGE, [3])
+model.testNbSubShapes(Wire_1, GeomAPI_Shape.VERTEX, [6])
index 3552530903fbaabb03e44826089f2f9de1b3ef7a..2738b36af765c7e79ebf019441b683cbd8aa0c6d 100644 (file)
@@ -21,7 +21,7 @@ The following property panel will be opened:
   
 Select one or several faces in viewer. Additionally, a face can be build by a closed wire or a set of edges composing a closed wire.
 
-It also allowed to select a whole sketch result from the object browser. In this case, the smallest closed contour of the sketch will be transformed to the planar face.
+It is also possible to select a whole sketch result from the object browser. In this case, the smallest closed contour of the sketch will be transformed to the planar face.
 
 **Apply** button creates faces.
 
index 9ca142f3a9dfb2a66b420f462443469aae9fdee8..8e6986ab4eaeba76a7fdf9b6b764bb7158f1480c 100644 (file)
Binary files a/src/BuildPlugin/doc/images/Edge.png and b/src/BuildPlugin/doc/images/Edge.png differ
index 3248c8c0360d537ed47d344e7b5cd387c6edd79b..d80e07aae0517624e9695341c3ed8bbda34f3a5a 100644 (file)
Binary files a/src/BuildPlugin/doc/images/Wire.png and b/src/BuildPlugin/doc/images/Wire.png differ
index e27d3025f0589f06768ee5d3d5691a923be04a8c..cfef4d604375abdc0d63d812907713fbe4a3a8cd 100644 (file)
@@ -18,7 +18,7 @@ The following property panel will be opened:
 .. centered::
   Create vertices
 
-Select one or several vertices in the viewer. It also allowed to select a whole sketch result in the object browser, then all start and end points of the sketch segments will be added into result. Checkbox **Compute intersections** forces to include intersection vertices of edges of the selected sketch.
+Select one or several vertices in the viewer. It is also possible to select a whole sketch result or feature in the object browser, then all start and end points of the sketch segments will be added into result. Checkbox **Compute intersections** forces to include intersection vertices of edges of the selected sketch.
 
 **Apply** button creates vertices.
 
index d0fca7a69c0bbf5717e402e4399b7a8b92b2f60f..70d14be3c504dbf4891b8a6f4b84d6bade8f459e 100644 (file)
@@ -1,7 +1,7 @@
 <source>
   <multi_selector id="base_objects"
-                  label="Segments and wires:"
-                  tooltip="Select edges on sketch, edges or wires objects."
+                  label="Segments, wires or sketches:"
+                  tooltip="Select edges on sketch (or the whole sketch), edges or wires objects."
                   shape_types="edges wires"
                   concealment="true">
     <validator id="BuildPlugin_ValidatorBaseForBuild" parameters="edge,wire,compound"/>
index a8770cb7c7059cf0a694b4f22f6fc9b1433f060e..e13a28446d23377c78b62132680e8888db9017dc 100644 (file)
@@ -163,4 +163,6 @@ ADD_UNIT_TESTS(
                TestGroupMoveAndSplit1.py
                TestGroupMoveAndSplit2.py
                TestGroupMoveAndSplit3.py
+               Test3114.py
+               Test18739.py
 )
index 57a2804e3e15aab47a86a9a2e90cab0138c31f4a..e6ae25802b36138aba4726743d50cb45a3675758 100644 (file)
@@ -96,7 +96,7 @@ bool CollectionPlugin_Group::customAction(const std::string& theActionId)
     std::list<ObjectPtr> aResults;
     for(int aNext = aList->size() - 1; aNext >= 0; aNext--) {
       AttributeSelectionPtr anOldAttr = aList->value(aNext);
-      if (anOldAttr->isInvalid() || !anOldAttr->context().get()) {// remove invalids
+      if (anOldAttr->isInvalid() || !anOldAttr->contextObject().get()) {// remove invalids
         aRemoved.insert(aNext);
         continue;
       }
diff --git a/src/CollectionPlugin/Test/Test18739.py b/src/CollectionPlugin/Test/Test18739.py
new file mode 100644 (file)
index 0000000..54b3568
--- /dev/null
@@ -0,0 +1,47 @@
+# 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
+#
+
+# Test of move to the end and split on LinearCopy: check that order in result is correct
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+model.addParameter(Part_1_doc, "d", "8")
+model.addParameter(Part_1_doc, "nb", "3")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(-34, 28, 1)
+SketchCircle_2 = Sketch_1.addCircle(-34, 28, 3)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchCircle_1.center(), SketchCircle_2.center())
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("WIRE", "Sketch_1/Face-SketchCircle_2_2f-SketchCircle_1_2r_wire_2")], model.selection(), 2, -1)
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("WIRE", "Sketch_1/Face-SketchCircle_2_2f-SketchCircle_1_2r_wire")], model.selection(), 5, 0)
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("COMPOUND", "all-in-Extrusion_1"), model.selection("COMPOUND", "all-in-Extrusion_2")])
+Group_1 = model.addGroup(Part_1_doc, "Solids", [model.selection("SOLID", "Partition_1_1_1")])
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("COMPSOLID", "Partition_1_1")], model.selection("EDGE", "PartSet/OX"), "d", "nb", model.selection("EDGE", "PartSet/OY"), "d", "nb")
+model.do()
+Part_1_doc.moveFeature(Group_1.feature(), LinearCopy_1.feature(), True)
+model.end()
+
+for a in range(9):
+  aGroup = Part_1_doc.objectByName("Features", "Group_1_" + str(a + 1))
+  aSelName = aGroup.data().selectionList("group_list").value(0).context().data().name()
+  assert(aSelName == "LinearCopy_1_1_" + str(a + 1) + "_1")
diff --git a/src/CollectionPlugin/Test/Test3114.py b/src/CollectionPlugin/Test/Test3114.py
new file mode 100644 (file)
index 0000000..3091af0
--- /dev/null
@@ -0,0 +1,71 @@
+# 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
+#
+
+# Test of move to the end and split of the group of whole feature selected and no new results appeared
+
+from salome.shaper import model
+
+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(45.19889502762433, 34.03867403314917, -47.9889502762431, 34.03867403314917)
+SketchLine_2 = Sketch_1.addLine(-47.9889502762431, 34.03867403314917, -47.9889502762431, -32.64364640883979)
+SketchLine_3 = Sketch_1.addLine(-47.9889502762431, -32.64364640883979, 45.19889502762433, -32.64364640883979)
+SketchLine_4 = Sketch_1.addLine(45.19889502762433, -32.64364640883979, 45.19889502762433, 34.03867403314917)
+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())
+SketchCircle_1 = Sketch_1.addCircle(-0.27900552486187, -3.9060773480663, 16.61196177134834)
+model.do()
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f-SketchCircle_1_2r")])
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("FACE", "Face_1_1")], model.selection("EDGE", "PartSet/OX"), 100)
+Recover_1 = model.addRecover(Part_1_doc, Translation_1, [Face_1.result()])
+Symmetry_1 = model.addSymmetry(Part_1_doc, [model.selection("FACE", "Translation_1_1")], model.selection("EDGE", "PartSet/OZ"), True)
+Extrusion_1_objects = [model.selection("FACE", "Recover_1_1"), model.selection("FACE", "Symmetry_1_1_1"), model.selection("FACE", "Symmetry_1_1_2")]
+Extrusion_1 = model.addExtrusion(Part_1_doc, Extrusion_1_objects, model.selection(), 50, 0)
+Group_3 = model.addGroup(Part_1_doc, "Edges", [model.selection("COMPOUND", "all-in-Extrusion_1")])
+Group_5 = model.addGroup(Part_1_doc, "Faces", [model.selection("COMPOUND", "all-in-Extrusion_1")])
+Group_6 = model.addGroup(Part_1_doc, "Solids", [model.selection("COMPOUND", "all-in-Extrusion_1")])
+LinearCopy_1_objects = [model.selection("SOLID", "Extrusion_1_3"), model.selection("SOLID", "Extrusion_1_1"), model.selection("SOLID", "Extrusion_1_2")]
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, LinearCopy_1_objects, model.selection("EDGE", "PartSet/OY"), 80, 2, model.selection("EDGE", "PartSet/OZ"), 70, 3)
+model.do()
+Part_1_doc.moveFeature(Group_3.feature(), LinearCopy_1.feature(), True)
+Part_1_doc.moveFeature(Group_5.feature(), LinearCopy_1.feature(), True)
+Part_1_doc.moveFeature(Group_6.feature(), LinearCopy_1.feature(), True)
+model.end()
+
+assert(Part_1_doc.size("Groups") == 3) # equal to the number of original groups
+
+# check names of results
+from ModelAPI import *
+
+aFactory = ModelAPI_Session.get().validators()
+for group in [Group_3, Group_5, Group_6]:
+  assert(aFactory.validate(group.feature()))
+  selectionList = group.feature().selectionList("group_list")
+  assert(selectionList.size() == 1)
+
+assert(model.checkPythonDump())
index fe97e96756c9990d7c3e6ff6e529b9dd2140492f..050b5d6a6fb45c426bc8c41f95123bc843daa2c4 100644 (file)
@@ -60,7 +60,15 @@ class Config_Prop
     ShortcutTree,
     BiColor,
     Background,
-    Directory
+    Directory,
+    Cursor
+  };
+
+  enum CursorType
+  {
+    ArrowCursor,
+    CrossCursor,
+    HandCursor
   };
 
   /** 
index 637e37b4e8de2f9edff675305ab4198034e474f2..bc369e6edd538353d587569c0625a2d57ade8664 100644 (file)
@@ -60,8 +60,14 @@ ConstructionPlugin_Plugin::ConstructionPlugin_Plugin()
     Config_Prop::IntSpin, SKETCH_WIDTH);
   Config_PropManager::registerProp(SKETCH_TAB_NAME, "angular_tolerance", "Angular tolerance",
     Config_Prop::DblSpin, "0.04");
+  Config_PropManager::registerProp(SKETCH_TAB_NAME, "spline_weight", "Default spline weight",
+    Config_Prop::DblSpin, "1.0");
   Config_PropManager::registerProp(SKETCH_TAB_NAME, "rotate_to_plane",
     "Rotate to plane when selected", Config_Prop::Boolean, "false");
+  Config_PropManager::registerProp(SKETCH_TAB_NAME, "operation_cursor",
+    "Cursor for Sketch operation", Config_Prop::Cursor, "0");
+  Config_PropManager::registerProp(SKETCH_TAB_NAME, "create_by_dragging",
+    "Create sketch enities by dragging", Config_Prop::Boolean, "false");
 
   // register this plugin
   ModelAPI_Session::get()->registerPlugin(this);
index 95f456fcee68b13f5c7268af80e395e5330de929..14b7592166c73e76496815e15c309d63c405e94b 100644 (file)
Binary files a/src/ConstructionPlugin/doc/images/Point2.png and b/src/ConstructionPlugin/doc/images/Point2.png differ
diff --git a/src/ConstructionPlugin/doc/images/Point3-1.png b/src/ConstructionPlugin/doc/images/Point3-1.png
new file mode 100644 (file)
index 0000000..6706067
Binary files /dev/null and b/src/ConstructionPlugin/doc/images/Point3-1.png differ
index 6d3d39aef6516258d2662c7e86f1ac87e368fe7a..4a3d48395c04f7673ac6beebd14ae83322aa4759 100644 (file)
Binary files a/src/ConstructionPlugin/doc/images/Point3.png and b/src/ConstructionPlugin/doc/images/Point3.png differ
diff --git a/src/ConstructionPlugin/doc/images/Point4-1.png b/src/ConstructionPlugin/doc/images/Point4-1.png
new file mode 100644 (file)
index 0000000..f12badf
Binary files /dev/null and b/src/ConstructionPlugin/doc/images/Point4-1.png differ
diff --git a/src/ConstructionPlugin/doc/images/Point4-2.png b/src/ConstructionPlugin/doc/images/Point4-2.png
new file mode 100644 (file)
index 0000000..e9794f6
Binary files /dev/null and b/src/ConstructionPlugin/doc/images/Point4-2.png differ
index 3f649d6370f952ae387530421d42bef02de65911..667263ae1bb99dfee4fe4313c1d7c233d7d2f6ec 100644 (file)
Binary files a/src/ConstructionPlugin/doc/images/Point4.png and b/src/ConstructionPlugin/doc/images/Point4.png differ
diff --git a/src/ConstructionPlugin/doc/images/Point5-1.png b/src/ConstructionPlugin/doc/images/Point5-1.png
new file mode 100644 (file)
index 0000000..795039d
Binary files /dev/null and b/src/ConstructionPlugin/doc/images/Point5-1.png differ
index 0580696c0bd5ff0ae558f78943d1a2b414328ab3..7cdec6d6b8d717e62eb9c58806b592313601243f 100644 (file)
Binary files a/src/ConstructionPlugin/doc/images/Point5.png and b/src/ConstructionPlugin/doc/images/Point5.png differ
index 5aac2ec2b30631d1f28da0deae6a4585d374b778..8d2693b49a05b04d1a0934c1c5f82b71c29c16d8 100644 (file)
@@ -76,7 +76,7 @@ By distance on edge
 .. centered::
    **Along an edge**
 
-To create a point, select an edge in a viewer and define a distance along the edge, where point will be defined. This distance can be defined by an absolute value or by a relative one as a ratio to the edge length. The direction of the edge can be reversed by the corresponded check box.
+To create a point, select an edge in a viewer and define a distance along the edge, where point will be defined. This distance can be defined by an absolute value or by a relative one as a ratio to the edge length. The direction of the edge can be reversed by the corresponding check-box.
 
 **TUI Commands**:
 
@@ -105,13 +105,26 @@ The Result of the operation will be a construction point created on edge:
 By projection on edge or plane
 ------------------------------
 
+To create a point by projection it is necessary to select an existing point or wertex which will be projected and an edge or a plane (planar face) on which it will be projected:
+
+**On an edge:**
+
 .. image:: images/Point3.png
    :align: center
        
 .. centered::
-   **By projection**
+   **By projection on an edge**
+
+**On a plane:**
+
+.. image:: images/Point3-1.png
+   :align: center
+       
+.. centered::
+   **By projection on a plane**
 
-To create a point, select an existing point or vertex and an edge or face. The new point will be created by projection of the selected point on the edge or face.
+
+The new point will be created by projection of the selected point on the selected object.
 
 **TUI Commands**:
 
@@ -138,17 +151,33 @@ The Result of the operation will be a construction point created by projection o
 By intersection of objects
 --------------------------
 
+A point can be created by intersection of selected objects:
+
+**Two edges**
+
 .. image:: images/Point4.png
    :align: center
        
 .. centered::
-   **Intersection of objects**
+   **Intersection of edges**
 
-To create a point, select:
+**Edge and plane**
 
-#. two edges,
-#. edge and plane,
-#. three planes
+.. image:: images/Point4-1.png
+   :align: center
+       
+.. centered::
+   **Intersection of and edge and a plane**
+
+In this case it is possible to define an offset from a plane along the plane normal for the created point.
+
+**Three planes**
+
+.. image:: images/Point4-2.png
+   :align: center
+       
+.. centered::
+   **Intersection of three planes**
 
 The new point will be defined by intersection of the selected objects.
 
@@ -179,13 +208,25 @@ The Result of the operation will be a construction point created by intersection
 By geometrical property of object
 ---------------------------------
 
+It is possible to use the following property of selected object:
+
+**A center of gravity**
+
 .. image:: images/Point5.png
    :align: center
        
 .. centered::
-   **By geometrical property**
+   **By center of gravity**
+
+**A center of circle or arc**
 
-In this case the new point can be defined as a center of gravity of the selected object or as a center of a circle. To create a point, select the desirable object.
+.. image:: images/Point5-1.png
+   :align: center
+       
+.. centered::
+   **By center of a circle**
+   
+To create a point, select a desirable object.
 
 **TUI Commands**:
 
index 2d764635a27b543c56fcc5f751cba48a17417922..87f51426c68b289fc29c24d1795f526da9a9bc58 100644 (file)
@@ -68,7 +68,7 @@ ExchangeAPI_Export::ExchangeAPI_Export(const std::shared_ptr<ModelAPI_Feature>&
   std::list<ModelHighAPI_Selection> aListOfOneSel;
   aListOfOneSel.push_back(theResult);
   fillAttribute(aListOfOneSel,
-    theFeature->selectionList(ExchangePlugin_ExportFeature::SELECTION_LIST_ID()));
+    theFeature->selectionList(ExchangePlugin_ExportFeature::XAO_SELECTION_LIST_ID()));
   execute();
   apply(); // finish operation to make sure the export is done on the current state of the history
 }
@@ -130,7 +130,7 @@ void ExchangeAPI_Export::dump(ModelHighAPI_Dumper& theDumper) const
     correctSeparators(aTmpXAOFile);
     theDumper << "exportToXAO(" << aDocName << ", '" << aTmpXAOFile << "'" ;
     AttributeSelectionListPtr aShapeSelected =
-      aBase->selectionList(ExchangePlugin_ExportFeature::SELECTION_LIST_ID());
+      aBase->selectionList(ExchangePlugin_ExportFeature::XAO_SELECTION_LIST_ID());
     if (aShapeSelected->isInitialized() && aShapeSelected->size() == 1) {
       theDumper<<", "<<aShapeSelected->value(0);
     }
index 3c6692e59eb3a8ab31d25c336d76c6f069337ae7..27b3961f0452db63fe110ca55cc72239242b9dba 100644 (file)
@@ -77,7 +77,21 @@ SET(PROJECT_LIBRARIES
 )
 SOURCE_GROUP ("Resource Files" FILES ${TEXT_RESOURCES})
 
+# default dump approaches (will be set if not initialized)
+SET(PYTHONDUMP_NAMING ON  CACHE BOOL "Dump named references to shapes")
+SET(PYTHONDUMP_GEO    ON  CACHE BOOL "Dump references to shapes by the geometric properties")
+SET(PYTHONDUMP_WEAK   OFF CACHE BOOL "Dump weak named references to shapes")
+
 ADD_DEFINITIONS(-DEXCHANGEPLUGIN_EXPORTS)
+IF(${PYTHONDUMP_NAMING})
+  ADD_DEFINITIONS(-DEXCHANGEPLUGIN_DUMP_NAMING)
+ENDIF()
+IF(${PYTHONDUMP_GEO})
+  ADD_DEFINITIONS(-DEXCHANGEPLUGIN_DUMP_GEO)
+ENDIF()
+IF(${PYTHONDUMP_WEAK})
+  ADD_DEFINITIONS(-DEXCHANGEPLUGIN_DUMP_WEAK)
+ENDIF()
 ADD_LIBRARY(ExchangePlugin MODULE ${PROJECT_SOURCES} ${PROJECT_HEADERS} ${XML_RESOURCES} ${TEXT_RESOURCES})
 
 TARGET_LINK_LIBRARIES(ExchangePlugin ${PROJECT_LIBRARIES})
@@ -92,6 +106,7 @@ ADD_UNIT_TESTS(
   TestExport.py
   Test2290.py
   Test2459.py
+  Test18710.py
   TestExportToXAOWithFields.py
   TestExportToXAOWithGroupNotUpdated.py
   TestExport_FiniteValidator.py
index 9ca46901b0afd9361f285cd821cd80a1615d06aa..e955db6548ec522c169b2aac63f24b1785da8c86 100644 (file)
 
 #include <Config_ModuleReader.h>
 
+#ifdef EXCHANGEPLUGIN_DUMP_NAMING
+static const bool THE_DUMP_NAMING = true;
+#else
+static const bool THE_DUMP_NAMING = false;
+#endif
+
+#ifdef EXCHANGEPLUGIN_DUMP_GEO
+static const bool THE_DUMP_GEO = true;
+#else
+static const bool THE_DUMP_GEO = false;
+#endif
+
+#ifdef EXCHANGEPLUGIN_DUMP_WEAK
+static const bool THE_DUMP_WEAK = true;
+#else
+static const bool THE_DUMP_WEAK = false;
+#endif
+
 
 ExchangePlugin_Dump::ExchangePlugin_Dump()
 {
@@ -50,9 +68,9 @@ void ExchangePlugin_Dump::initAttributes()
   data()->addAttribute(EXPORT_VARIABLES_ID(), ModelAPI_AttributeBoolean::typeId());
 
   // default values
-  boolean(TOPOLOGICAL_NAMING_DUMP_ID())->setValue(true);
-  boolean(GEOMETRIC_DUMP_ID())->setValue(true);
-  boolean(WEAK_NAMING_DUMP_ID())->setValue(false);
+  boolean(TOPOLOGICAL_NAMING_DUMP_ID())->setValue(THE_DUMP_NAMING);
+  boolean(GEOMETRIC_DUMP_ID())->setValue(THE_DUMP_GEO);
+  boolean(WEAK_NAMING_DUMP_ID())->setValue(THE_DUMP_WEAK);
   boolean(EXPORT_VARIABLES_ID())->setValue(false);
 }
 
index 02a12e88f0044aa254db331d6be268fbabf071ad..93500cc5e7660a345a7aa6e851878269123e23fe 100644 (file)
@@ -94,6 +94,8 @@ void ExchangePlugin_ExportFeature::initAttributes()
     ModelAPI_AttributeString::typeId());
   data()->addAttribute(ExchangePlugin_ExportFeature::XAO_GEOMETRY_NAME_ID(),
     ModelAPI_AttributeString::typeId());
+  data()->addAttribute(ExchangePlugin_ExportFeature::XAO_SELECTION_LIST_ID(),
+    ModelAPI_AttributeSelectionList::typeId());
 
   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(),
     ExchangePlugin_ExportFeature::XAO_FILE_PATH_ID());
@@ -101,6 +103,21 @@ void ExchangePlugin_ExportFeature::initAttributes()
     ExchangePlugin_ExportFeature::XAO_AUTHOR_ID());
   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(),
     ExchangePlugin_ExportFeature::XAO_GEOMETRY_NAME_ID());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(),
+    ExchangePlugin_ExportFeature::XAO_SELECTION_LIST_ID());
+
+  // to support previous version of document, move the selection list
+  // if the type of export operation is XAO
+  AttributeStringPtr aTypeAttr = string(EXPORT_TYPE_ID());
+  if (aTypeAttr->isInitialized() && aTypeAttr->value() == "XAO") {
+    bool aWasBlocked = data()->blockSendAttributeUpdated(true, false);
+    AttributeSelectionListPtr aSelList = selectionList(SELECTION_LIST_ID());
+    AttributeSelectionListPtr aXAOSelList = selectionList(XAO_SELECTION_LIST_ID());
+    if (aSelList->size() > 0 && aXAOSelList->size() == 0)
+      aSelList->copyTo(aXAOSelList);
+    aSelList->clear();
+    data()->blockSendAttributeUpdated(aWasBlocked, false);
+  }
 }
 
 void ExchangePlugin_ExportFeature::attributeChanged(const std::string& theID)
@@ -316,7 +333,7 @@ void ExchangePlugin_ExportFeature::exportXAO(const std::string& theFileName)
   std::list<DocumentPtr> aDocuments; /// documents of Parts selected and used in export
   std::map<DocumentPtr, GeomTrsfPtr> aDocTrsf; /// translation of the part
 
-  AttributeSelectionListPtr aSelection = selectionList(SELECTION_LIST_ID());
+  AttributeSelectionListPtr aSelection = selectionList(XAO_SELECTION_LIST_ID());
   bool aIsSelection = aSelection->isInitialized() && aSelection->size() > 0;
   if (aIsSelection) { // a mode for export to geom result by result
     for(int a = 0; a < aSelection->size(); a++) {
@@ -577,7 +594,7 @@ bool ExchangePlugin_ExportFeature::isMacro() const
 
   if (aFormat == "XAO") { // on export to GEOM the selection attribute is filled - this is
                           // an exceptional case where export to XAO feature must be kept
-    AttributeSelectionListPtr aList = aThis->selectionList(SELECTION_LIST_ID());
+    AttributeSelectionListPtr aList = aThis->selectionList(XAO_SELECTION_LIST_ID());
     return !aList->isInitialized() || aList->size() == 0;
   }
   return true;
index c0a79a8c5ea092bcbbba0d7fff00d3a97f0d6d7a..aa24b3d4980d62c72e4a07851f7b4d3ac5067cd1 100644 (file)
@@ -72,6 +72,12 @@ public:
     static const std::string MY_SELECTION_LIST_ID("selection_list");
     return MY_SELECTION_LIST_ID;
   }
+  /// attribute name of xao selection list
+  inline static const std::string& XAO_SELECTION_LIST_ID()
+  {
+    static const std::string MY_SELECTION_LIST_ID("xao_selection_list");
+    return MY_SELECTION_LIST_ID;
+  }
   /// attribute name of author for XAO format
   inline static const std::string& XAO_AUTHOR_ID()
   {
index 2957ce7217b00469d4ba67f3bcacdd0e1d3fe0e5..a0889ea2738a3c5bdf96354a5306e033cb6fd2db 100644 (file)
@@ -47,7 +47,10 @@ static void collectConstructions(DocumentPtr theDocument, std::list<FeaturePtr>&
 // Returns true if all features can be exported.
 static bool verifyExport(const std::list<FeaturePtr>& theFeatures,
                          std::list<FeaturePtr>& theExternalReferences,
-                         std::list<FeaturePtr>& theExportedParts);
+                         std::list<FeaturePtr>& theExportedParts,
+                         std::list<FeaturePtr>& theReferredParts);
+// Collect names of features as a single string
+static std::string namesOfFeatures(const std::list<FeaturePtr>& theFeatures);
 
 
 ExchangePlugin_ExportPart::ExchangePlugin_ExportPart()
@@ -95,38 +98,30 @@ void ExchangePlugin_ExportPart::execute()
   if (aFeaturesToExport.back()->getKind() == ExchangePlugin_ExportPart::ID())
     aFeaturesToExport.pop_back();
 
-  std::list<FeaturePtr> anExternalLinks, aReferredParts;
-  if (!verifyExport(aFeaturesToExport, anExternalLinks, aReferredParts)) {
+  std::list<FeaturePtr> anExternalLinks, anExportedParts, aReferredParts;
+  if (!verifyExport(aFeaturesToExport, anExternalLinks, anExportedParts, 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 aListOfFeatures = namesOfFeatures(anExternalLinks);
 
       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();
+      Events_InfoMessage(getKind(), aMessage).arg(aListOfFeatures).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 aListOfParts = namesOfFeatures(aReferredParts);
 
       std::string aMessage = "The selected results were created using references "
-                             "to results of Parts %1. Please, remove these references "
+                             "to the 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();
+      Events_InfoMessage(getKind(), aMessage).arg(aListOfParts).send();
+    }
+    if (!anExportedParts.empty()) {
+      std::string aListOfParts = namesOfFeatures(anExportedParts);
+
+      std::string aMessage = "The export of Part's result is forbidden (%1).";
+      Events_InfoMessage(getKind(), aMessage).arg(aListOfParts).send();
     }
     // should not export anything
     aFeaturesToExport.clear();
@@ -244,7 +239,8 @@ void collectConstructions(DocumentPtr theDocument, std::list<FeaturePtr>& theExp
 
 bool verifyExport(const std::list<FeaturePtr>& theFeatures,
                   std::list<FeaturePtr>& theExternalReferences,
-                  std::list<FeaturePtr>& theExportedParts)
+                  std::list<FeaturePtr>& theExportedParts,
+                  std::list<FeaturePtr>& theReferredParts)
 {
   for (std::list<FeaturePtr>::const_iterator anIt = theFeatures.begin();
        anIt != theFeatures.end(); ++anIt) {
@@ -268,11 +264,23 @@ bool verifyExport(const std::list<FeaturePtr>& theFeatures,
             theExternalReferences.push_back(*anIt);
           // feature refers to result of a part
           if (aFeature->getKind() == PartSetPlugin_Part::ID())
-            theExportedParts.push_back(*anIt);
+            theReferredParts.push_back(*anIt);
         }
       }
     }
   }
 
-  return theExternalReferences.empty() && theExportedParts.empty();
+  return theExternalReferences.empty() && theExportedParts.empty() && theReferredParts.empty();
+}
+
+std::string namesOfFeatures(const std::list<FeaturePtr>& theFeatures)
+{
+  std::ostringstream aListOfFeatures;
+  for (std::list<FeaturePtr>::const_iterator anIt = theFeatures.begin();
+       anIt != theFeatures.end(); ++anIt) {
+    if (anIt != theFeatures.begin())
+      aListOfFeatures << ", ";
+    aListOfFeatures << "'" << (*anIt)->name() << "'";
+  }
+  return aListOfFeatures.str();
 }
index 992e2357b8bce6ab31b7e56b96adf3e75b1578a2..2a9f07f8f50d335330915c19d8e9a92c17ddad44 100644 (file)
       <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>
+      <source>The selected results were created using references to the 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 the results of Parts: %1. Please, remove these references or select another sub-set of results to be able to export.</translation>
+    </message>
+    <message>
+      <source>The export of Part's result is forbidden (%1).</source>
+      <translation>The export of Part&apos;s result is forbidden (%1).</translation>
     </message>
   </context>
 
index 930ee4c560a235bdafdb5deea898b7fbe1516cb2..ee798af4b49c025cf70498fbc242fac553c96a24 100644 (file)
     </message>
   </context>
 
+  <context>
+    <name>Export:ExchangePlugin_ExportPart</name>
+    <message>
+      <source>Cannot save the document.</source>
+      <translation>Impossible d&apos;enregistrer le document.</translation>
+    </message>
+    <message>
+      <source>Selected features cannot be exported from the document.</source>
+      <translation>Les fonctions sélectionnées ne peuvent pas être exportées à partir du 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>Les résultats sélectionnés ont été créés à l&apos;aide de références externes en dehors de cette pièce à partir des fonctions %1. Veuillez supprimer ces références ou sélectionner un autre sous-ensemble de résultats pour pouvoir exporter.</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>Les résultats sélectionnés ont été créés en utilisant des références aux résultats des pièces: %1. Veuillez supprimer ces références ou sélectionner un autre sous-ensemble de résultats pour pouvoir exporter.</translation>
+    </message>
+    <message>
+      <source>The export of Part's result is forbidden (%1).</source>
+      <translation>L&apos;exportation du résultat de la pièce est interdite (%1).</translation>
+    </message>
+  </context>
+
+  <context>
+    <name>Import:ExchangePlugin_ImportPart</name>
+    <message>
+      <source>Cannot import the document.</source>
+      <translation>Impossible d&apos;importer le document.</translation>
+    </message>
+  </context>
+
 </TS>
diff --git a/src/ExchangePlugin/Test/Test18710.py b/src/ExchangePlugin/Test/Test18710.py
new file mode 100644 (file)
index 0000000..93a1fb9
--- /dev/null
@@ -0,0 +1,56 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+from GeomAPI import *
+from ModelAPI import *
+from SketchAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Export_1 = model.exportToXAO(Part_1_doc, '/tmp/shaper_vcnhioqf.xao', model.selection("SOLID", "Cylinder_1_1"), 'XAO')
+Export_1.setName("XAO")
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+model.end()
+
+model.testNbResults(Part_1, 1)
+model.testNbSubResults(Part_1, [0])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.FACE, [9])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.EDGE, [30])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.VERTEX, [60])
+model.testResultsVolumes(Part_1, [1785.39816339744857])
+
+model.begin()
+ModelAPI.removeFeaturesAndReferences(FeatureSet([Cylinder_1.feature()]))
+model.end()
+
+model.testNbResults(Part_1, 1)
+model.testNbSubResults(Part_1, [0])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.FACE, [6])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.EDGE, [24])
+model.testNbSubShapes(Part_1, GeomAPI_Shape.VERTEX, [48])
+model.testResultsVolumes(Part_1, [1000])
+
+assert(model.checkPythonDump())
index acd6222f5e1cc4c674c701072acaf6b93b2f4f21..2b6df755fbc79463e186f9871574952685c7c31d 100644 (file)
@@ -48,6 +48,9 @@ SET(PROJECT_HEADERS
   FeaturesAPI_Translation.h
   FeaturesAPI_Union.h
   FeaturesAPI_FusionFaces.h
+  FeaturesAPI_Copy.h
+  FeaturesAPI_ImportResult.h
+  FeaturesAPI_Defeaturing.h
 )
 
 SET(PROJECT_SOURCES
@@ -78,6 +81,9 @@ SET(PROJECT_SOURCES
   FeaturesAPI_Translation.cpp
   FeaturesAPI_Union.cpp
   FeaturesAPI_FusionFaces.cpp
+  FeaturesAPI_Copy.cpp
+  FeaturesAPI_ImportResult.cpp
+  FeaturesAPI_Defeaturing.cpp
 )
 
 SET(PROJECT_LIBRARIES
index c0a747b2f65bf52d060c472d250209cf053f792a..a77c6a1c4b1692a44593081af896bf84ba17b4dd 100644 (file)
@@ -77,6 +77,9 @@
 %shared_ptr(FeaturesAPI_Union)
 %shared_ptr(FeaturesAPI_FusionFaces)
 %shared_ptr(FeaturesAPI_RemoveResults)
+%shared_ptr(FeaturesAPI_Copy)
+%shared_ptr(FeaturesAPI_ImportResult)
+%shared_ptr(FeaturesAPI_Defeaturing)
 
 
 %typecheck(SWIG_TYPECHECK_POINTER) std::pair<std::list<ModelHighAPI_Selection>, bool>, const std::pair<std::list<ModelHighAPI_Selection>, bool> & {
 %include "FeaturesAPI_BooleanSmash.h"
 %include "FeaturesAPI_BooleanFill.h"
 %include "FeaturesAPI_Chamfer.h"
+%include "FeaturesAPI_Defeaturing.h"
 %include "FeaturesAPI_Extrusion.h"
 %include "FeaturesAPI_ExtrusionBoolean.h"
 %include "FeaturesAPI_Fillet.h"
 %include "FeaturesAPI_Union.h"
 %include "FeaturesAPI_FusionFaces.h"
 %include "FeaturesAPI_RemoveResults.h"
+%include "FeaturesAPI_Copy.h"
+%include "FeaturesAPI_ImportResult.h"
diff --git a/src/FeaturesAPI/FeaturesAPI_Copy.cpp b/src/FeaturesAPI/FeaturesAPI_Copy.cpp
new file mode 100644 (file)
index 0000000..5ec81ab
--- /dev/null
@@ -0,0 +1,80 @@
+// 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 "FeaturesAPI_Copy.h"
+
+#include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Tools.h>
+
+//================================================================================================
+FeaturesAPI_Copy::FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature)
+: ModelHighAPI_Interface(theFeature)
+{
+  initialize();
+}
+
+//================================================================================================
+FeaturesAPI_Copy::FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                     const std::list<ModelHighAPI_Selection>& theObjects,
+                                     const int theNumber)
+: ModelHighAPI_Interface(theFeature)
+{
+  if(initialize()) {
+    setNumber(theNumber);
+    setObjects(theObjects);
+  }
+}
+
+//================================================================================================
+FeaturesAPI_Copy::~FeaturesAPI_Copy() {}
+
+//=================================================================================================
+void FeaturesAPI_Copy::setObjects(const std::list<ModelHighAPI_Selection>& theObjects)
+{
+  fillAttribute(theObjects, myobjects);
+  execute();
+}
+//=================================================================================================
+void FeaturesAPI_Copy::setNumber(const int theNumber)
+{
+  fillAttribute(theNumber, mynumber);
+  execute();
+}
+
+//=================================================================================================
+void FeaturesAPI_Copy::dump(ModelHighAPI_Dumper& theDumper) const
+{
+  FeaturePtr aBase = feature();
+  const std::string& aDocName = theDumper.name(aBase->document());
+
+  AttributeSelectionListPtr anObjects = aBase->selectionList(FeaturesPlugin_Copy::OBJECTS());
+  AttributeIntegerPtr aNumber = aBase->integer(FeaturesPlugin_Copy::NUMBER());
+
+  theDumper << aBase << " = model.addCopy("
+            << aDocName << ", " << anObjects << ", " << aNumber << ")" << std::endl;
+}
+
+//=================================================================================================
+CopyPtr addCopy(const std::shared_ptr<ModelAPI_Document>& thePart,
+                const std::list<ModelHighAPI_Selection>& theObjects,
+                const int theNumber)
+{
+  std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(FeaturesAPI_Copy::ID());
+  return CopyPtr(new FeaturesAPI_Copy(aFeature, theObjects, theNumber));
+}
diff --git a/src/FeaturesAPI/FeaturesAPI_Copy.h b/src/FeaturesAPI/FeaturesAPI_Copy.h
new file mode 100644 (file)
index 0000000..1fa43de
--- /dev/null
@@ -0,0 +1,78 @@
+// 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 FeaturesAPI_Copy_H_
+#define FeaturesAPI_Copy_H_
+
+#include "FeaturesAPI.h"
+
+#include <FeaturesPlugin_Copy.h>
+
+#include <ModelHighAPI_Interface.h>
+#include <ModelHighAPI_Macro.h>
+
+class ModelHighAPI_Dumper;
+class ModelHighAPI_Selection;
+
+/// \class FeaturesAPI_Copy
+/// \ingroup CPPHighAPI
+/// \brief Interface for Copy feature.
+class FeaturesAPI_Copy: public ModelHighAPI_Interface
+{
+public:
+  /// Constructor without values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature);
+
+  /// Constructor with values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_Copy(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                            const std::list<ModelHighAPI_Selection>& theBaseObjects,
+                            const int theNumber);
+
+  /// Destructor.
+  FEATURESAPI_EXPORT virtual ~FeaturesAPI_Copy();
+
+  INTERFACE_2(FeaturesPlugin_Copy::ID(),
+              objects, FeaturesPlugin_Copy::OBJECTS(),
+              ModelAPI_AttributeSelectionList, /** Source objects */,
+              number, FeaturesPlugin_Copy::NUMBER(),
+              ModelAPI_AttributeInteger, /** Number of copies */)
+
+  /// Modify objects attribute of the feature.
+  FEATURESAPI_EXPORT void setObjects(const std::list<ModelHighAPI_Selection>& theBaseObjects);
+
+  /// Modify number of copies attribute of the feature.
+  FEATURESAPI_EXPORT void setNumber(const int theNumber);
+
+  /// Dump wrapped feature
+  FEATURESAPI_EXPORT virtual void dump(ModelHighAPI_Dumper& theDumper) const;
+};
+
+/// Pointer on Copy object.
+typedef std::shared_ptr<FeaturesAPI_Copy> CopyPtr;
+
+/// \ingroup CPPHighAPI
+/// \brief Create Copy feature.
+FEATURESAPI_EXPORT
+CopyPtr addCopy(const std::shared_ptr<ModelAPI_Document>& thePart,
+                const std::list<ModelHighAPI_Selection>& theObjects,
+                const int theNumber);
+
+#endif // FeaturesAPI_Copy_H_
diff --git a/src/FeaturesAPI/FeaturesAPI_Defeaturing.cpp b/src/FeaturesAPI/FeaturesAPI_Defeaturing.cpp
new file mode 100644 (file)
index 0000000..5accac9
--- /dev/null
@@ -0,0 +1,79 @@
+// Copyright (C) 2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "FeaturesAPI_Defeaturing.h"
+
+#include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Tools.h>
+
+FeaturesAPI_Defeaturing::FeaturesAPI_Defeaturing(
+    const std::shared_ptr<ModelAPI_Feature>& theFeature)
+  : ModelHighAPI_Interface(theFeature)
+{
+  initialize();
+}
+
+FeaturesAPI_Defeaturing::FeaturesAPI_Defeaturing(
+    const std::shared_ptr<ModelAPI_Feature>& theFeature,
+    const std::list<ModelHighAPI_Selection>& theFacesToRemove)
+  : ModelHighAPI_Interface(theFeature)
+{
+  if (initialize())
+    setFaces(theFacesToRemove);
+}
+
+FeaturesAPI_Defeaturing::~FeaturesAPI_Defeaturing()
+{
+}
+
+void FeaturesAPI_Defeaturing::setFaces(const std::list<ModelHighAPI_Selection>& theFacesToRemove)
+{
+  mybaseObjects->clear();
+  fillAttribute(theFacesToRemove, mybaseObjects);
+
+  execIfBaseNotEmpty();
+}
+
+void FeaturesAPI_Defeaturing::dump(ModelHighAPI_Dumper& theDumper) const
+{
+  FeaturePtr aBase = feature();
+  const std::string& aDocName = theDumper.name(aBase->document());
+
+  AttributeSelectionListPtr anAttrObjects =
+    aBase->selectionList(FeaturesPlugin_Defeaturing::OBJECT_LIST_ID());
+
+  theDumper << aBase << " = model.addDefeaturing(" << aDocName << ", "
+            << anAttrObjects << ")" << std::endl;
+}
+
+void FeaturesAPI_Defeaturing::execIfBaseNotEmpty()
+{
+  if (mybaseObjects->size() > 0)
+    execute();
+}
+
+
+//==================================================================================================
+
+DefeaturingPtr addDefeaturing(const std::shared_ptr<ModelAPI_Document>& thePart,
+                              const std::list<ModelHighAPI_Selection>& theFaces)
+{
+  std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(FeaturesAPI_Defeaturing::ID());
+  return DefeaturingPtr(new FeaturesAPI_Defeaturing(aFeature, theFaces));
+}
diff --git a/src/FeaturesAPI/FeaturesAPI_Defeaturing.h b/src/FeaturesAPI/FeaturesAPI_Defeaturing.h
new file mode 100644 (file)
index 0000000..960934d
--- /dev/null
@@ -0,0 +1,76 @@
+// Copyright (C) 2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef FeaturesAPI_Defeaturing_H_
+#define FeaturesAPI_Defeaturing_H_
+
+#include "FeaturesAPI.h"
+
+#include <FeaturesPlugin_Defeaturing.h>
+
+#include <ModelHighAPI_Interface.h>
+#include <ModelHighAPI_Macro.h>
+
+class ModelHighAPI_Selection;
+
+/// \class FeaturesAPI_Defeaturing
+/// \ingroup CPPHighAPI
+/// \brief Interface for the Defeaturing feature.
+class FeaturesAPI_Defeaturing: public ModelHighAPI_Interface
+{
+public:
+  /// Constructor without values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_Defeaturing(const std::shared_ptr<ModelAPI_Feature>& theFeature);
+
+  /// Constructor with values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_Defeaturing(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                   const std::list<ModelHighAPI_Selection>& theFacesToRemove);
+
+  /// Destructor.
+  FEATURESAPI_EXPORT
+  virtual ~FeaturesAPI_Defeaturing();
+
+  INTERFACE_1(FeaturesPlugin_Defeaturing::ID(),
+              baseObjects, FeaturesPlugin_Defeaturing::OBJECT_LIST_ID(),
+              ModelAPI_AttributeSelectionList, /** Base objects */)
+
+  /// Modify faces to be removed.
+  FEATURESAPI_EXPORT
+  void setFaces(const std::list<ModelHighAPI_Selection>& theFacesToRemove);
+
+  /// Dump wrapped feature
+  FEATURESAPI_EXPORT
+  virtual void dump(ModelHighAPI_Dumper& theDumper) const;
+
+private:
+  void execIfBaseNotEmpty();
+};
+
+/// Pointer on the Defeaturing object.
+typedef std::shared_ptr<FeaturesAPI_Defeaturing> DefeaturingPtr;
+
+/// \ingroup CPPHighAPI
+/// \brief Create Defeaturing feature.
+FEATURESAPI_EXPORT
+DefeaturingPtr addDefeaturing(const std::shared_ptr<ModelAPI_Document>& thePart,
+                              const std::list<ModelHighAPI_Selection>& theFaces);
+
+#endif // FeaturesAPI_Defeaturing_H_
diff --git a/src/FeaturesAPI/FeaturesAPI_ImportResult.cpp b/src/FeaturesAPI/FeaturesAPI_ImportResult.cpp
new file mode 100644 (file)
index 0000000..cf72de3
--- /dev/null
@@ -0,0 +1,70 @@
+// 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 "FeaturesAPI_ImportResult.h"
+
+#include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Tools.h>
+
+//================================================================================================
+FeaturesAPI_ImportResult::FeaturesAPI_ImportResult(
+  const std::shared_ptr<ModelAPI_Feature>& theFeature) : ModelHighAPI_Interface(theFeature)
+{
+  initialize();
+}
+
+//================================================================================================
+FeaturesAPI_ImportResult::FeaturesAPI_ImportResult(
+  const std::shared_ptr<ModelAPI_Feature>& theFeature,
+  const std::list<ModelHighAPI_Selection>& theObjects) : ModelHighAPI_Interface(theFeature)
+{
+  if(initialize()) {
+    setObjects(theObjects);
+  }
+}
+
+//=================================================================================================
+FeaturesAPI_ImportResult::~FeaturesAPI_ImportResult() {}
+
+//=================================================================================================
+void FeaturesAPI_ImportResult::setObjects(const std::list<ModelHighAPI_Selection>& theObjects)
+{
+  fillAttribute(theObjects, myobjects);
+  execute();
+}
+
+//=================================================================================================
+void FeaturesAPI_ImportResult::dump(ModelHighAPI_Dumper& theDumper) const
+{
+  FeaturePtr aBase = feature();
+  const std::string& aDocName = theDumper.name(aBase->document());
+  AttributeSelectionListPtr anObjects =
+    aBase->selectionList(FeaturesPlugin_ImportResult::OBJECTS());
+
+  theDumper << aBase << " = model.addImportResult("
+            << aDocName << ", " << anObjects << ")" << std::endl;
+}
+
+//=================================================================================================
+ImportResultPtr addImportResult(const std::shared_ptr<ModelAPI_Document>& thePart,
+                                const std::list<ModelHighAPI_Selection>& theObjects)
+{
+  std::shared_ptr<ModelAPI_Feature> aFeature = thePart->addFeature(FeaturesAPI_ImportResult::ID());
+  return ImportResultPtr(new FeaturesAPI_ImportResult(aFeature, theObjects));
+}
diff --git a/src/FeaturesAPI/FeaturesAPI_ImportResult.h b/src/FeaturesAPI/FeaturesAPI_ImportResult.h
new file mode 100644 (file)
index 0000000..a461f3d
--- /dev/null
@@ -0,0 +1,71 @@
+// 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 FeaturesAPI_ImportResult_H_
+#define FeaturesAPI_ImportResult_H_
+
+#include "FeaturesAPI.h"
+
+#include <FeaturesPlugin_ImportResult.h>
+
+#include <ModelHighAPI_Interface.h>
+#include <ModelHighAPI_Macro.h>
+
+class ModelHighAPI_Dumper;
+class ModelHighAPI_Selection;
+
+/// \class FeaturesAPI_ImportResult
+/// \ingroup CPPHighAPI
+/// \brief Interface for ImportResult feature.
+class FeaturesAPI_ImportResult: public ModelHighAPI_Interface
+{
+public:
+  /// Constructor without values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_ImportResult(const std::shared_ptr<ModelAPI_Feature>& theFeature);
+
+  /// Constructor with values.
+  FEATURESAPI_EXPORT
+  explicit FeaturesAPI_ImportResult(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                    const std::list<ModelHighAPI_Selection>& theBaseObjects);
+
+  /// Destructor.
+  FEATURESAPI_EXPORT virtual ~FeaturesAPI_ImportResult();
+
+  INTERFACE_1(FeaturesPlugin_ImportResult::ID(),
+              objects, FeaturesPlugin_ImportResult::OBJECTS(),
+              ModelAPI_AttributeSelectionList, /** Source objects */)
+
+  /// Modify objects attribute of the feature.
+  FEATURESAPI_EXPORT void setObjects(const std::list<ModelHighAPI_Selection>& theBaseObjects);
+
+  /// Dump wrapped feature
+  FEATURESAPI_EXPORT virtual void dump(ModelHighAPI_Dumper& theDumper) const;
+};
+
+/// Pointer on ImportResult object.
+typedef std::shared_ptr<FeaturesAPI_ImportResult> ImportResultPtr;
+
+/// \ingroup CPPHighAPI
+/// \brief Create ImportResult feature.
+FEATURESAPI_EXPORT
+ImportResultPtr addImportResult(const std::shared_ptr<ModelAPI_Document>& thePart,
+                                const std::list<ModelHighAPI_Selection>& theObjects);
+
+#endif // FeaturesAPI_ImportResult_H_
index cd71216fae72dacf4ad6f159760cf424ba686105..af2866c3ee671b5caf762ccc84ad8aaa7e80e27a 100644 (file)
@@ -29,6 +29,7 @@
   #include "FeaturesAPI_BooleanSmash.h"
   #include "FeaturesAPI_BooleanFill.h"
   #include "FeaturesAPI_Chamfer.h"
+  #include "FeaturesAPI_Defeaturing.h"
   #include "FeaturesAPI_Extrusion.h"
   #include "FeaturesAPI_ExtrusionBoolean.h"
   #include "FeaturesAPI_Fillet.h"
@@ -50,5 +51,7 @@
   #include "FeaturesAPI_Union.h"
   #include "FeaturesAPI_FusionFaces.h"
   #include "FeaturesAPI_RemoveResults.h"
+  #include "FeaturesAPI_Copy.h"
+  #include "FeaturesAPI_ImportResult.h"
 
 #endif // FeaturesAPI_swig_H_
index ea14d9ea6191b21c6dcb092626702e2aecae0a7a..45dd74930e910a40b131809ca3047df80771cdfb 100644 (file)
@@ -61,6 +61,9 @@ SET(PROJECT_HEADERS
     FeaturesPlugin_FusionFaces.h
     FeaturesPlugin_RemoveResults.h
     FeaturesPlugin_Chamfer.h
+    FeaturesPlugin_Copy.h
+    FeaturesPlugin_ImportResult.h
+    FeaturesPlugin_Defeaturing.h
 )
 
 SET(PROJECT_SOURCES
@@ -103,6 +106,9 @@ SET(PROJECT_SOURCES
     FeaturesPlugin_FusionFaces.cpp
     FeaturesPlugin_RemoveResults.cpp
     FeaturesPlugin_Chamfer.cpp
+    FeaturesPlugin_Copy.cpp
+    FeaturesPlugin_ImportResult.cpp
+    FeaturesPlugin_Defeaturing.cpp
 )
 
 SET(XML_RESOURCES
@@ -135,6 +141,9 @@ SET(XML_RESOURCES
   measurement_widget.xml
   fusion_faces_widget.xml
   chamfer_widget.xml
+  copy_widget.xml
+  import_result_widget.xml
+  defeaturing_widget.xml
 )
 
 SET(TEXT_RESOURCES
@@ -541,4 +550,23 @@ ADD_UNIT_TESTS(TestExtrusion.py
                Test3033.py
                Test3076.py
                Test17909.py
+               TestCopyFeature.py
+               TestCopyFeatureMoveGroupOfFeature.py
+               TestCopyMoveResult.py
+               TestCopyMoveSubShapes.py
+               TestCopyNames.py
+               TestCopySubShapes.py
+               TestCopyWholeFeature.py
+               TestImportResult.py
+               TestDefeaturing_ErrorMsg.py
+               TestDefeaturing_OnSolid1.py
+               TestDefeaturing_OnSolid2.py
+               TestDefeaturing_OnSolid3.py
+               TestDefeaturing_OnCompsolid1.py
+               TestDefeaturing_OnCompsolid2.py
+               TestDefeaturing_OnCompsolid3.py
+               TestDefeaturing_OnCompound.py
+               Test3137_1.py
+               Test3137_2.py
+               Test2918.py
 )
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp b/src/FeaturesPlugin/FeaturesPlugin_Copy.cpp
new file mode 100644 (file)
index 0000000..76db7fa
--- /dev/null
@@ -0,0 +1,155 @@
+// Copyright (C) 2017-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 "FeaturesPlugin_Copy.h"
+
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_Tools.h>
+
+#include <GeomAlgoAPI_Copy.h>
+#include <GeomAlgoAPI_Tools.h>
+#include <GeomAPI_ShapeExplorer.h>
+
+#include <sstream>
+
+void FeaturesPlugin_Copy::initAttributes()
+{
+  data()->addAttribute(OBJECTS(), ModelAPI_AttributeSelectionList::typeId());
+  data()->addAttribute(NUMBER(), ModelAPI_AttributeInteger::typeId());
+}
+
+static GeomShapePtr shapeOfSelection(AttributeSelectionPtr theSel) {
+  GeomShapePtr aResult;
+  FeaturePtr aSelFeature = theSel->contextFeature();
+  if (aSelFeature.get()) {
+    if (aSelFeature->results().empty()) // if selected feature has no results, make nothing
+      return aResult;
+    if (aSelFeature->results().size() == 1) { // for one sub-result don't make compound
+      aResult = aSelFeature->firstResult()->shape();
+    }
+  }
+  if (!aResult.get())
+    aResult = theSel->value();
+  if (!aResult.get()) {
+    if (theSel->context().get())
+      aResult = theSel->context()->shape();
+  }
+  return aResult;
+}
+
+void FeaturesPlugin_Copy::execute()
+{
+  int aCopiesNum = integer(NUMBER())->value();
+  AttributeSelectionListPtr aList = selectionList(OBJECTS());
+  int aResultIndex = 0;
+  std::set<std::string> anExistingNames; // to avoid names duplication
+  for(int aCopy = 0; aCopy < aCopiesNum; aCopy++) {
+    for (int aSelIndex = 0; aSelIndex < aList->size(); aSelIndex++) {
+      AttributeSelectionPtr aSel = aList->value(aSelIndex);
+      GeomShapePtr aShape = shapeOfSelection(aSel);
+      if (!aShape.get())
+        continue;
+      std::shared_ptr<GeomAlgoAPI_Copy> aCopyBuilder(new GeomAlgoAPI_Copy(aShape, false, false));
+      std::string anError;
+      if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aCopyBuilder, getKind(), anError)) {
+        setError(anError);
+        return;
+      }
+      GeomShapePtr aResult = aCopyBuilder->shape();
+
+      std::string aBaseName = aSel->context() ? aSel->context()->data()->name() :
+        aSel->contextFeature()->firstResult()->data()->name();
+      std::string aName;
+      int anInd = 0;
+      do {
+        anInd++;
+        std::ostringstream aNameStr;
+        aNameStr << aBaseName << "_" << (aCopy + anInd);
+        aName = aNameStr.str();
+      } while (anExistingNames.count(aName));
+      anExistingNames.insert(aName);
+
+      std::shared_ptr<ModelAPI_ResultBody> aResultBody =
+        document()->createBody(data(), aResultIndex);
+      aResultBody->data()->setName(aName);
+      // to make sub-results also names with a similar name temporarily rename the feature
+      std::string anOrigName = name();
+      data()->setName(aBaseName);
+      aResultBody->store(aResult);
+      data()->setName(anOrigName);
+      aResultBody->loadFirstLevel(aResult, "Copy");
+      setResult(aResultBody, aResultIndex++);
+    }
+  }
+  removeResults(aResultIndex);
+}
+
+void FeaturesPlugin_Copy::getCopies(
+  ObjectPtr theContext, std::shared_ptr<GeomAPI_Shape> theValue,
+  std::list<ObjectPtr>& theCopyContext, std::list<std::shared_ptr<GeomAPI_Shape> >& theCopyVals)
+{
+  ResultPtr aContextRes = std::dynamic_pointer_cast<ModelAPI_Result>(theContext);
+  GeomShapePtr aGroupValue = theValue.get() ? theValue : aContextRes->shape();
+
+  AttributeSelectionListPtr aList = selectionList(OBJECTS());
+  std::list<ResultPtr>::const_iterator aResIter = results().cbegin();
+  while(aResIter != results().cend()) { // do as long as many iterations
+    for (int aSelIndex = 0; aSelIndex < aList->size(); aSelIndex++) {
+      if (aResIter == results().cend()) // no more results corresponding to the selection
+        return;
+      AttributeSelectionPtr aSel = aList->value(aSelIndex);
+      GeomShapePtr aShape = shapeOfSelection(aSel);
+      if (!aShape.get())
+        continue;
+
+      if (aShape->isSubShape(aGroupValue, false)) { // group value is subshape of copied => copy
+        // search the same result in the copy by the same index of sub-shape in the shape
+        GeomAPI_ShapeExplorer anOrigExp(aShape, aGroupValue->shapeType());
+        GeomAPI_ShapeExplorer aCopyShape((*aResIter)->shape(), aGroupValue->shapeType());
+        for(; anOrigExp.more(); anOrigExp.next(), aCopyShape.next()) {
+          if (anOrigExp.current()->isSame(aGroupValue)) {
+            // searching for sub-result if it is composite result, but context-not
+            ResultPtr aResContext = *aResIter;
+            if (aContextRes->shape()->shapeType() > (*aResIter)->shape()->shapeType()) {
+              ResultBodyPtr aResBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aResContext);
+              if (aResBody.get()) {
+                std::list<ResultPtr> aSubs;
+                ModelAPI_Tools::allSubs(aResBody, aSubs, true);
+                std::list<ResultPtr>::iterator aSubIter = aSubs.begin();
+                for(; aSubIter != aSubs.end(); aSubIter++) {
+                  GeomShapePtr aSubShape = (*aSubIter)->shape();
+                  if (aSubShape.get() && aSubShape->isSubShape(aCopyShape.current(), false)) {
+                    aResContext = *aSubIter;
+                    break;
+                  }
+                }
+              }
+            }
+            theCopyContext.push_back(aResContext);
+            theCopyVals.push_back(aResContext->shape()->isSame(
+              aCopyShape.current()) ? GeomShapePtr() : aCopyShape.current());
+            break;
+          }
+        }
+      }
+      aResIter++;
+    }
+  }
+}
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Copy.h b/src/FeaturesPlugin/FeaturesPlugin_Copy.h
new file mode 100644 (file)
index 0000000..b500b51
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright (C) 2017-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 FeaturesPlugin_Copy_H_
+#define FeaturesPlugin_Copy_H_
+
+#include "FeaturesPlugin.h"
+
+#include <ModelAPI_Feature.h>
+
+/// \class FeaturesPlugin_Copy
+/// \ingroup Plugins
+/// \brief This feature copies the selected results and sub-results (for the whole feature selected
+///        all results of this feature are copied). The referenced arguments of this feature are
+///        not concealed. The difference with \94Recover\94 feature is that not concealed results may
+///        be selected and in the history behavior: the \93Move to the End\94 of groups will move to
+///        all copy-results.
+
+class FeaturesPlugin_Copy : public ModelAPI_Feature, public ModelAPI_FeatureCopyInterface
+{
+public:
+  /// Feature kind.
+  inline static const std::string& ID()
+  {
+    static const std::string MY_ID("Copy");
+    return MY_ID;
+  }
+
+  /// \return the kind of a feature.
+  FEATURESPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = FeaturesPlugin_Copy::ID();
+    return MY_KIND;
+  }
+
+  /// Selection list attribute that contains all copied shapes selection.
+  inline static const std::string& OBJECTS()
+  {
+    static const std::string MY_OBJECTS("objects");
+    return MY_OBJECTS;
+  }
+  /// Integer attribute that contains the number of resulting copies needed
+  inline static const std::string NUMBER()
+  {
+    static std::string MY_NUMBER("number");
+    return MY_NUMBER;
+  }
+
+  /// Performs the algorithm and stores results it in the data structure.
+  FEATURESPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes.
+  FEATURESPLUGIN_EXPORT virtual void initAttributes();
+
+  /// To update the group feature which is moved over this copy feature (to add copies to selection)
+  FEATURESPLUGIN_EXPORT virtual void getCopies(
+    ObjectPtr theContext, std::shared_ptr<GeomAPI_Shape> theValue,
+    std::list<ObjectPtr>& theCopyContext, std::list<std::shared_ptr<GeomAPI_Shape> >& theCopyVals);
+
+  /// Use plugin manager for features creation.
+  FeaturesPlugin_Copy() {}
+};
+
+#endif
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Defeaturing.cpp b/src/FeaturesPlugin/FeaturesPlugin_Defeaturing.cpp
new file mode 100644 (file)
index 0000000..104667e
--- /dev/null
@@ -0,0 +1,115 @@
+// Copyright (C) 2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <FeaturesPlugin_Defeaturing.h>
+#include <FeaturesPlugin_Tools.h>
+
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_Tools.h>
+
+#include <GeomAPI_ShapeExplorer.h>
+
+#include <GeomAlgoAPI_CompoundBuilder.h>
+#include <GeomAlgoAPI_Defeaturing.h>
+#include <GeomAlgoAPI_MakeShapeList.h>
+#include <GeomAlgoAPI_Tools.h>
+
+#include <unordered_map>
+
+
+FeaturesPlugin_Defeaturing::FeaturesPlugin_Defeaturing()
+{
+}
+
+void FeaturesPlugin_Defeaturing::initAttributes()
+{
+  data()->addAttribute(OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList::typeId());
+}
+
+
+void FeaturesPlugin_Defeaturing::execute()
+{
+  typedef std::unordered_map<GeomShapePtr, ListOfShape,
+                             GeomAPI_Shape::Hash, GeomAPI_Shape::Equal> SolidFaces;
+  SolidFaces aBodiesAndFacesToRemove;
+
+  // getting objects and sort them according to parent solids
+  AttributeSelectionListPtr anObjectsSelList = selectionList(OBJECT_LIST_ID());
+  for (int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); ++anObjectsIndex) {
+    AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
+    GeomShapePtr anObject = anObjectAttr->value();
+    if (!anObject)
+      return;
+
+    ResultPtr aContext = anObjectAttr->context();
+    if (!aContext.get())
+      return;
+
+    ResultBodyPtr aCtxOwner = ModelAPI_Tools::bodyOwner(aContext, true);
+    GeomShapePtr aParent = aCtxOwner ? aCtxOwner->shape() : aContext->shape();
+    aBodiesAndFacesToRemove[aParent].push_back(anObject);
+  }
+
+  // Perform Defeaturing algorithm
+  GeomAlgoAPI_MakeShapeList aMakeShapeList;
+  std::shared_ptr<GeomAlgoAPI_Defeaturing> anAlgo;
+  int aResultIndex = 0;
+  std::string anError;
+
+  std::vector<FeaturesPlugin_Tools::ResultBaseAlgo> aResultBaseAlgoList;
+  ListOfShape anOriginalShapesList, aResultShapesList;
+
+  for (SolidFaces::iterator anIt = aBodiesAndFacesToRemove.begin();
+       anIt != aBodiesAndFacesToRemove.end(); ++anIt) {
+    GeomShapePtr aParent = anIt->first;
+    anAlgo.reset(new GeomAlgoAPI_Defeaturing(aParent, anIt->second));
+    if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(anAlgo, getKind(), anError)) {
+      setError(anError);
+      return;
+    }
+
+    GeomShapePtr aResult = anAlgo->shape();
+    ListOfShape aBaseShapes;
+    for (GeomAPI_ShapeExplorer anExp(aParent, GeomAPI_Shape::SOLID); anExp.more(); anExp.next())
+      aBaseShapes.push_back(anExp.current());
+
+    std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
+    FeaturesPlugin_Tools::loadModifiedShapes(aResultBody, aBaseShapes, ListOfShape(),
+                                             anAlgo, aResult, "Defeaturing");
+
+    setResult(aResultBody, aResultIndex);
+    aResultIndex++;
+
+    FeaturesPlugin_Tools::ResultBaseAlgo aRBA;
+    aRBA.resultBody = aResultBody;
+    aRBA.baseShape = aParent;
+    aRBA.makeShape = anAlgo;
+    aResultBaseAlgoList.push_back(aRBA);
+    aResultShapesList.push_back(aResult);
+    anOriginalShapesList.insert(anOriginalShapesList.end(), aBaseShapes.begin(), aBaseShapes.end());
+  }
+
+  // Store deleted shapes after all results has been proceeded. This is to avoid issue when in one
+  // result shape has been deleted, but in another it was modified or stayed.
+  GeomShapePtr aResultShapesCompound = GeomAlgoAPI_CompoundBuilder::compound(aResultShapesList);
+  FeaturesPlugin_Tools::loadDeletedShapes(aResultBaseAlgoList,
+      anOriginalShapesList, aResultShapesCompound);
+
+  removeResults(aResultIndex);
+}
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Defeaturing.h b/src/FeaturesPlugin/FeaturesPlugin_Defeaturing.h
new file mode 100644 (file)
index 0000000..c703e02
--- /dev/null
@@ -0,0 +1,74 @@
+// Copyright (C) 2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef FeaturesPlugin_Defeaturing_H_
+#define FeaturesPlugin_Defeaturing_H_
+
+#include "FeaturesPlugin.h"
+
+#include <ModelAPI_Feature.h>
+
+class GeomAlgoAPI_MakeShape;
+class GeomAPI_Shape;
+
+/// \class FeaturesPlugin_Defeaturing
+/// \ingroup Plugins
+/// \brief Feature for the removal of the unwanted parts or features from the model.
+class FeaturesPlugin_Defeaturing : public ModelAPI_Feature
+{
+public:
+  /// Feature kind.
+  inline static const std::string& ID()
+  {
+    static const std::string MY_ID("Defeaturing");
+    return MY_ID;
+  }
+
+  /// \return the kind of a feature.
+  FEATURESPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = FeaturesPlugin_Defeaturing::ID();
+    return MY_KIND;
+  }
+
+  /// Attribute name of main objects.
+  inline static const std::string& OBJECT_LIST_ID()
+  {
+    static const std::string MY_OBJECT_LIST_ID("main_objects");
+    return MY_OBJECT_LIST_ID;
+  }
+
+  /// Performs the defeaturing algorithm and stores it in the data structure.
+  FEATURESPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes.
+  FEATURESPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Use plugin manager for features creation.
+  FeaturesPlugin_Defeaturing();
+
+private:
+  /// Load Naming data structure of the feature to the document
+  void loadNamingDS(std::shared_ptr<ModelAPI_ResultBody> theResultBody,
+                    const std::shared_ptr<GeomAPI_Shape> theBaseShape,
+                    const std::shared_ptr<GeomAPI_Shape> theResultShape,
+                    const std::shared_ptr<GeomAlgoAPI_MakeShape>& theMakeShape);
+};
+
+#endif
diff --git a/src/FeaturesPlugin/FeaturesPlugin_ImportResult.cpp b/src/FeaturesPlugin/FeaturesPlugin_ImportResult.cpp
new file mode 100644 (file)
index 0000000..80e61c0
--- /dev/null
@@ -0,0 +1,117 @@
+// Copyright (C) 2017-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 "FeaturesPlugin_ImportResult.h"
+
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_ResultBody.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_ResultPart.h>
+#include <Events_InfoMessage.h>
+
+void FeaturesPlugin_ImportResult::initAttributes()
+{
+  data()->addAttribute(OBJECTS(), ModelAPI_AttributeSelectionList::typeId());
+}
+
+void FeaturesPlugin_ImportResult::execute()
+{
+  AttributeSelectionListPtr aList = selectionList(OBJECTS());
+  int aResultIndex = 0;
+  for (int aSelIndex = 0; aSelIndex < aList->size(); aSelIndex++) {
+    AttributeSelectionPtr aSel = aList->value(aSelIndex);
+    ResultPtr aContext = aSel->context();
+    if (!aContext.get())
+      continue;
+    GeomShapePtr aShape = aContext->shape();
+    if (!aShape.get() || aShape->isNull())
+      continue;
+    std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data(), aResultIndex);
+    aResultBody->store(aShape);
+    aResultBody->loadFirstLevel(aShape, "ImportResult");
+    setResult(aResultBody, aResultIndex++);
+  }
+  removeResults(aResultIndex);
+}
+
+bool FeaturesPlugin_ValidatorImportResults::isValid(const AttributePtr& theAttribute,
+  const std::list<std::string>&, Events_InfoMessage& theError) const
+{
+  AttributeSelectionListPtr aList =
+    std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
+  if (aList->size() == 0) {
+    // LCOV_EXCL_START
+    theError = "Please select sources.";
+    return false;
+    // LCOV_EXCL_STOP
+  }
+  // store documents in the Part-fesatures in order to check
+  // the selection is located in the previous documents
+  std::map<DocumentPtr, int> aDocIndices;
+  DocumentPtr aRoot = ModelAPI_Session::get()->moduleDocument();
+  int aNum = aRoot->size(ModelAPI_Feature::group());
+  for (int a = 0; a < aNum; a++) {
+    FeaturePtr aFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(
+      aRoot->object(ModelAPI_Feature::group(), a));
+    if (aFeat.get() && aFeat->data() && aFeat->data()->isValid() && aFeat->getKind() == "Part" &&
+      aFeat->results().size()) {
+      ResultPartPtr aPart = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aFeat->firstResult());
+      if (aPart.get() && aPart->partDoc()) {
+        aDocIndices[aPart->partDoc()] = a;
+      }
+    }
+  }
+  int aThisIndex = aDocIndices[aList->owner()->document()];
+  for (int aSelIndex = 0; aSelIndex < aList->size(); aSelIndex++) {
+    AttributeSelectionPtr aSel = aList->value(aSelIndex);
+    ResultPtr aContext = aSel->context();
+    if (!aContext.get()) {
+      // LCOV_EXCL_START
+      theError = "Empty context.";
+      return false;
+      // LCOV_EXCL_STOP
+    }
+    GeomShapePtr aShape = aSel->value();
+    if (aShape.get() && !aShape->isNull() && !aShape->isSame(aContext->shape())) {
+      // LCOV_EXCL_START
+      theError = "Import results does not support selection of sub-shapes";
+      return false;
+      // LCOV_EXCL_STOP
+    }
+    ResultBodyPtr aBody = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aContext);
+    if (!aBody.get()) {
+      // LCOV_EXCL_START
+      theError = "Only results may be selected.";
+      return false;
+      // LCOV_EXCL_STOP
+    }
+    if (!aBody->shape()) {
+      // LCOV_EXCL_START
+      theError = "Result is empty.";
+      return false;
+      // LCOV_EXCL_STOP
+    }
+    int aBodyIndex = aDocIndices[aBody->document()];
+    if (aBodyIndex >= aThisIndex) {
+      theError = "The selected result must be located in earlier part.";
+      return false;
+    }
+  }
+  return true;
+}
diff --git a/src/FeaturesPlugin/FeaturesPlugin_ImportResult.h b/src/FeaturesPlugin/FeaturesPlugin_ImportResult.h
new file mode 100644 (file)
index 0000000..4b5df61
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright (C) 2017-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 FeaturesPlugin_ImportResult_H_
+#define FeaturesPlugin_ImportResult_H_
+
+#include "FeaturesPlugin.h"
+
+#include <ModelAPI_Feature.h>
+#include <ModelAPI_AttributeValidator.h>
+
+/// \class FeaturesPlugin_ImportResult
+/// \ingroup Plugins
+/// \brief The Import Result feature allows the user to import one or several results
+///        from another Part.
+
+class FeaturesPlugin_ImportResult : public ModelAPI_Feature
+{
+public:
+  /// Feature kind.
+  inline static const std::string& ID()
+  {
+    static const std::string MY_ID("ImportResult");
+    return MY_ID;
+  }
+
+  /// \return the kind of a feature.
+  FEATURESPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = FeaturesPlugin_ImportResult::ID();
+    return MY_KIND;
+  }
+
+  /// Selection list attribute that contains all copied shapes selection.
+  inline static const std::string& OBJECTS()
+  {
+    static const std::string MY_OBJECTS("objects");
+    return MY_OBJECTS;
+  }
+
+  /// Performs the algorithm and stores results it in the data structure.
+  FEATURESPLUGIN_EXPORT virtual void execute();
+
+  /// Request for initialization of data model of the feature: adding all attributes.
+  FEATURESPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Use plugin manager for features creation.
+  FeaturesPlugin_ImportResult() {}
+};
+
+/// \class FeaturesPlugin_ValidatorImportResults
+/// \ingroup Validators
+/// \brief A validator for selection of objects that may be imported:
+///        from another part, result bodies.
+class FeaturesPlugin_ValidatorImportResults : public ModelAPI_AttributeValidator
+{
+public:
+  //! \return True if selection is valid.
+  //! \param[in] theAttribute the checked attribute.
+  //! \param[in] theArguments arguments of the attribute.
+  //! \param[out] theError error message.
+  virtual bool isValid(const AttributePtr& theAttribute,
+    const std::list<std::string>& theArguments, Events_InfoMessage& theError) const;
+};
+
+#endif
index 64286a9d7e6d8b70f52cbdfca3d2debc18a95d97..a11fc4af13b7a7265c2f9e195948c93c0eca4f6e 100644 (file)
@@ -227,8 +227,8 @@ void FeaturesPlugin_MultiRotation::performRotation1D()
                                                aListOfRotationAlgo, aCompound, "Rotated");
 
       setResult(aResultBody, aResultIndex);
+      aResultIndex++;
     }
-    aResultIndex++;
   }
 
   // Remove the rest results if there were produced in the previous pass.
@@ -412,8 +412,8 @@ void FeaturesPlugin_MultiRotation::performRotation2D()
       loadNamingDS2(aListOfTranslationAlgo, aResultBody, aBaseShape);
       loadNamingDS3(aListOfRotationAlgo, aResultBody, aBaseShape, nbRadial);
       setResult(aResultBody, aResultIndex);
+      aResultIndex++;
     }
-    aResultIndex++;
   }
 
   // Remove the rest results if there were produced in the previous pass.
index 10368e91e643f8d0cac8bb6486611ee036cc5994..9f311df137ea6909fa03c345b30f955ab1617064 100644 (file)
@@ -209,8 +209,8 @@ void FeaturesPlugin_MultiTranslation::performOneDirection()
                                                aListOfTranslationAlgo, aCompound, "Translated");
 
       setResult(aResultBody, aResultIndex);
+      aResultIndex++;
     }
-    aResultIndex++;
   }
 
   // Remove the rest results if there were produced in the previous pass.
@@ -410,8 +410,8 @@ void FeaturesPlugin_MultiTranslation::performTwoDirection()
                                                aListOfTranslationAlgo, aCompound, "Translated");
 
       setResult(aResultBody, aResultIndex);
+      aResultIndex++;
     }
-    aResultIndex++;
   }
 
   // Remove the rest results if there were produced in the previous pass.
index 7973434462db3f8af12183e9c78748163ed714b5..5f226c11b5d615d0fde56686f2628a7532dcf467 100644 (file)
@@ -25,6 +25,7 @@
 #include <FeaturesPlugin_BooleanSmash.h>
 #include <FeaturesPlugin_BooleanFill.h>
 #include <FeaturesPlugin_Chamfer.h>
+#include <FeaturesPlugin_Defeaturing.h>
 #include <FeaturesPlugin_Extrusion.h>
 #include <FeaturesPlugin_ExtrusionCut.h>
 #include <FeaturesPlugin_ExtrusionFuse.h>
@@ -48,6 +49,8 @@
 #include <FeaturesPlugin_Union.h>
 #include <FeaturesPlugin_FusionFaces.h>
 #include <FeaturesPlugin_RemoveResults.h>
+#include <FeaturesPlugin_Copy.h>
+#include <FeaturesPlugin_ImportResult.h>
 #include <FeaturesPlugin_ValidatorTransform.h>
 #include <FeaturesPlugin_Validators.h>
 
@@ -114,6 +117,10 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin()
                               new FeaturesPlugin_ValidatorBooleanCommonSelection);
   aFactory->registerValidator("FeaturesPlugin_ValidatorBooleanCommonArguments",
                               new FeaturesPlugin_ValidatorBooleanCommonArguments);
+  aFactory->registerValidator("FeaturesPlugin_ValidatorImportResults",
+                              new FeaturesPlugin_ValidatorImportResults);
+  aFactory->registerValidator("FeaturesPlugin_ValidatorDefeaturingSelection",
+                              new FeaturesPlugin_ValidatorDefeaturingSelection);
 
   // register this plugin
   ModelAPI_Session::get()->registerPlugin(this);
@@ -179,8 +186,15 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(std::string theFeatureID)
     return FeaturePtr(new FeaturesPlugin_RemoveResults);
   } else if (theFeatureID == FeaturesPlugin_Chamfer::ID()) {
     return FeaturePtr(new FeaturesPlugin_Chamfer);
+  } else if (theFeatureID == FeaturesPlugin_Copy::ID()) {
+    return FeaturePtr(new FeaturesPlugin_Copy);
+  } else if (theFeatureID == FeaturesPlugin_ImportResult::ID()) {
+    return FeaturePtr(new FeaturesPlugin_ImportResult);
+  } else if (theFeatureID == FeaturesPlugin_Defeaturing::ID()) {
+    return FeaturePtr(new FeaturesPlugin_Defeaturing);
   }
 
+
   // feature of such kind is not found
   return FeaturePtr();
 }
index 07db4dd86f32d32371a2dede20b1e61811bde22f..6426d94739bba43533d8efb7910e3ad6f1be5298 100644 (file)
@@ -1877,3 +1877,42 @@ bool FeaturesPlugin_ValidatorBooleanCommonArguments::isNotObligatory(
   return false;
 }
 // LCOV_EXCL_STOP
+
+//==================================================================================================
+bool FeaturesPlugin_ValidatorDefeaturingSelection::isValid(
+    const AttributePtr& theAttribute,
+    const std::list<std::string>& theArguments,
+    Events_InfoMessage& theError) const
+{
+  AttributeSelectionListPtr anAttrSelectionList =
+      std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
+  if (!anAttrSelectionList.get()) {
+    // LCOV_EXCL_START
+    theError = "Error: This validator can only work with selection list attributes.";
+    return false;
+    // LCOV_EXCL_STOP
+  }
+
+  // Check selected entities are sub-shapes of solid or compsolid
+  GeomShapePtr aBaseSolid;
+  for (int anIndex = 0; anIndex < anAttrSelectionList->size(); ++anIndex) {
+    AttributeSelectionPtr anAttrSelection = anAttrSelectionList->value(anIndex);
+    if (!anAttrSelection.get()) {
+      theError = "Error: Empty attribute selection.";
+      return false;
+    }
+    ResultPtr aContext = anAttrSelection->context();
+    if (!aContext.get()) {
+      theError = "Error: Empty selection context.";
+      return false;
+    }
+
+    GeomShapePtr aContextShape = aContext->shape();
+    if (aContextShape->shapeType() != GeomAPI_Shape::SOLID) {
+      theError = "Error: Not all selected shapes are sub-shapes of solids.";
+      return false;
+    }
+  }
+
+  return true;
+}
index c2ec597dfbc60dba004f592bc404ef48abd0fddc..da3222d1ec7e0c4a71cbd58c73f91936ebb00de4 100644 (file)
@@ -419,4 +419,20 @@ public:
   virtual bool isNotObligatory(std::string theFeature, std::string theAttribute);
 };
 
+/// \class FeaturesPlugin_ValidatorDefeaturingSelection
+/// \ingroup Validators
+/// \brief Validates selection for fillet operation.
+class FeaturesPlugin_ValidatorDefeaturingSelection : public ModelAPI_AttributeValidator
+{
+public:
+  /// \return True if the attribute is valid. It checks whether the selection
+  /// is acceptable for boolean operation.
+  /// \param[in] theAttribute an attribute to check.
+  /// \param[in] theArguments a filter parameters.
+  /// \param[out] theError error message.
+  virtual bool isValid(const AttributePtr& theAttribute,
+                       const std::list<std::string>& theArguments,
+                       Events_InfoMessage& theError) const;
+};
+
 #endif
index 95b2e83e2bd3bc8f2e193af7c251c02fc1efbc64..71f2863f671249a95bed5358f25a8386d9871607 100644 (file)
       <source>Recover</source>
       <translation>Récupérer</translation>
     </message>
+    <message>
+      <source>Copy</source>
+      <translation>Copie</translation>
+    </message>
+    <message>
+      <source>Import Result</source>
+      <translation>Importer le résultat</translation>
+    </message>
     <message>
       <source>Remove Sub-Shapes</source>
       <translation>Supprimer les sous-formes</translation>
     </message>
   </context>
 
+  <!-- Defeaturing -->
+  <context>
+    <name>Defeaturing</name>
+    <message>
+      <source>Defeaturing</source>
+      <translation>Vaincre</translation>
+    </message>
+    <message>
+      <source>Faces to remove</source>
+      <translation>Visages à retirer</translation>
+    </message>
+    <message>
+      <source>Select faces</source>
+      <translation>Sélectionnez des faces</translation>
+    </message>
+  </context>
+  <context>
+    <name>Defeaturing:FeaturesPlugin_ValidatorDefeaturingSelection</name>
+    <message>
+      <source>Error: This validator can only work with selection list attributes.</source>
+      <translation>Erreur: ce validateur ne peut fonctionner qu&apos;avec des attributs de liste de sélection.</translation>
+    </message>
+    <message>
+      <source>Error: Empty attribute selection.</source>
+      <translation>La sélection d&apos;attribut est vide.</translation>
+    </message>
+    <message>
+      <source>Error: Empty selection context.</source>
+      <translation>Erreur: contexte de sélection vide.</translation>
+    </message>
+    <message>
+      <source>Error: Not all selected shapes are sub-shapes of solids.</source>
+      <translation>Erreur: toutes les formes sélectionnées ne sont pas des sous-formes de solides.</translation>
+    </message>
+  </context>
+
   <!-- Extrusion -->
   <context>
     <name>Extrusion</name>
     </message>
   </context>
 
+  <!-- Copy -->
+  <context>
+    <name>Copy</name>
+    <message>
+      <source>Copy</source>
+      <translation>Copie</translation>
+    </message>
+    <message>
+      <source>Copies results or sub-results</source>
+      <translation>Copie les résultats ou les sous-résultats</translation>
+    </message>
+  </context>
+  <context>
+    <name>Recover:objects</name>
+    <message>
+      <source>Sources:</source>
+      <translation>Sources:</translation>
+    </message>
+    <message>
+      <source>Select copied objects</source>
+      <translation>Sélectionnez les objets copiés</translation>
+    </message>
+  </context>
+  <context>
+    <name>Recover:number</name>
+    <message>
+      <source>Nb copies</source>
+      <translation>Nb de copies</translation>
+    </message>
+    <message>
+      <source>Number of copies</source>
+      <translation>Nombre de copies</translation>
+    </message>
+  </context>
+
+  <!-- Import result -->
+  <context>
+    <name>ImportResult</name>
+    <message>
+      <source>ImportResult</source>
+      <translation>Importer le résultat</translation>
+    </message>
+    <message>
+      <source>Copies results from other parts</source>
+      <translation>Copie les résultats d'autres pièces</translation>
+    </message>
+  </context>
+  <context>
+    <name>ImportResult:objects</name>
+    <message>
+      <source>Sources:</source>
+      <translation>Sources:</translation>
+    </message>
+    <message>
+      <source>Select copied results</source>
+      <translation>Sélectionnez les résultats copiés</translation>
+    </message>
+  </context>
+
   <!-- Remove_SubShapes -->
   <context>
     <name>Remove_SubShapes</name>
index 6901086373903272fa6a87cae2a6026480e62c67..4e52f45275f6f58f31e0b04506eea85d44d97148 100644 (file)
       <translation>Выберите вспомогательные объекты.</translation>
     </message>
   </context>
+
+  <!-- Defeaturing -->
+  <context>
+    <name>Defeaturing</name>
+    <message>
+      <source>Defeaturing</source>
+      <translation>Удаление фичеров</translation>
+    </message>
+    <message>
+      <source>Faces to remove</source>
+      <translation>Удаляемые грани</translation>
+    </message>
+    <message>
+      <source>Select faces</source>
+      <translation>Выберите грани</translation>
+    </message>
+  </context>
+  <context>
+    <name>Defeaturing:FeaturesPlugin_ValidatorDefeaturingSelection</name>
+    <message>
+      <source>Error: This validator can only work with selection list attributes.</source>
+      <translation>Ошибка: валидатор поддерживает только аттрибуты типа SelectionList.</translation>
+    </message>
+    <message>
+      <source>Error: Empty attribute selection.</source>
+      <translation>Ошибка: незаполненный аттрибут.</translation>
+    </message>
+    <message>
+      <source>Error: Empty selection context.</source>
+      <translation>Ошибка: пустой контекст.</translation>
+    </message>
+    <message>
+      <source>Error: Not all selected shapes are sub-shapes of solids.</source>
+      <translation>Ошибка: не все выбранные объекты являются подэлементами твердых тел.</translation>
+    </message>
+  </context>
 </TS>
diff --git a/src/FeaturesPlugin/Test/Test2918.py b/src/FeaturesPlugin/Test/Test2918.py
new file mode 100644 (file)
index 0000000..530acf2
--- /dev/null
@@ -0,0 +1,328 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomAPI import *
+from SketchAPI import *
+
+from salome.shaper import model
+
+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("XOZ"))
+SketchLine_1 = Sketch_1.addLine(6.188, 9.028, 6.188, -3.45)
+SketchLine_2 = Sketch_1.addLine(6.188, -3.45, 0, -3.45)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(0, -3.45, 0, -6.45)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(0, -6.45, 7.782, -6.45)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(7.782, -6.45, 7.782, -3.45)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchLine_6 = Sketch_1.addLine(7.782, -3.45, 6.538, -3.45)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(6.538, -3.45, 6.538, -2.4)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchLine_8 = Sketch_1.addLine(6.538, -2.4, 6.376, -2.25)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchLine_9 = Sketch_1.addLine(6.376, -2.25, 6.376, 9.028)
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint())
+SketchLine_10 = Sketch_1.addLine(6.376, 9.028, 6.188, 9.028)
+SketchLine_10.setAuxiliary(True)
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_10.endPoint())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_10.result())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_1.result())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_9.result())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_3.result())
+SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_5.result())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_2.result())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_6.result())
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_4.result())
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchLine_7.startPoint(), SketchLine_2.result())
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_11 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_3.startPoint(), SketchLine_11.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_5.result(), 3)
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchAPI_Line(SketchLine_11).startPoint(), SketchLine_1.result(), 6.188, True)
+SketchConstraintVertical_5 = Sketch_1.setVertical(SketchLine_7.result())
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_12 = SketchProjection_2.createdFeature()
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_9.endPoint(), SketchLine_12.result(), 9.028, True)
+SketchConstraintDistance_3 = Sketch_1.setDistance(SketchLine_2.startPoint(), SketchLine_9.result(), 0.188, True)
+SketchConstraintDistance_4 = Sketch_1.setDistance(SketchLine_2.startPoint(), SketchLine_7.result(), 0.35, True)
+SketchConstraintDistanceVertical_1 = Sketch_1.setVerticalDistance(SketchLine_7.endPoint(), SketchLine_8.endPoint(), 0.15)
+SketchConstraintDistance_5 = Sketch_1.setDistance(SketchLine_5.endPoint(), SketchLine_12.result(), 3.45, True)
+SketchConstraintDistance_6 = Sketch_1.setDistance(SketchLine_8.endPoint(), SketchLine_12.result(), 2.25, True)
+SketchLine_13 = Sketch_1.addLine(7.032, -4.05, 5.532, -4.05)
+SketchLine_14 = Sketch_1.addLine(5.532, -4.05, 5.532, -5.85)
+SketchLine_15 = Sketch_1.addLine(5.532, -5.85, 7.032, -5.85)
+SketchLine_16 = Sketch_1.addLine(7.032, -5.85, 7.032, -4.05)
+SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchLine_16.endPoint(), SketchLine_13.startPoint())
+SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchLine_13.endPoint(), SketchLine_14.startPoint())
+SketchConstraintCoincidence_15 = Sketch_1.setCoincident(SketchLine_14.endPoint(), SketchLine_15.startPoint())
+SketchConstraintCoincidence_16 = Sketch_1.setCoincident(SketchLine_15.endPoint(), SketchLine_16.startPoint())
+SketchConstraintHorizontal_5 = Sketch_1.setHorizontal(SketchLine_13.result())
+SketchConstraintVertical_6 = Sketch_1.setVertical(SketchLine_14.result())
+SketchConstraintHorizontal_6 = Sketch_1.setHorizontal(SketchLine_15.result())
+SketchConstraintVertical_7 = Sketch_1.setVertical(SketchLine_16.result())
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_14.result(), 1.8)
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_15.result(), 1.5)
+SketchLine_17 = Sketch_1.addLine(6.282, -4.05, 6.282, 9.028)
+SketchLine_17.setAuxiliary(True)
+SketchConstraintCoincidence_17 = Sketch_1.setCoincident(SketchLine_17.startPoint(), SketchLine_13.result())
+SketchConstraintCoincidence_18 = Sketch_1.setCoincident(SketchLine_17.endPoint(), SketchLine_10.result())
+SketchConstraintVertical_8 = Sketch_1.setVertical(SketchLine_17.result())
+SketchConstraintMiddle_1 = Sketch_1.setMiddlePoint(SketchLine_17.startPoint(), SketchLine_13.result())
+SketchConstraintDistance_7 = Sketch_1.setDistance(SketchLine_17.startPoint(), SketchLine_5.result(), 1.5, True)
+SketchConstraintMiddle_2 = Sketch_1.setMiddlePoint(SketchLine_10.result(), SketchLine_17.endPoint())
+SketchConstraintDistance_8 = Sketch_1.setDistance(SketchLine_14.startPoint(), SketchLine_2.result(), 0.6, True)
+SketchLine_18 = Sketch_1.addLine(6.376, 9.028, 6.535, 9.187)
+SketchConstraintCoincidence_19 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_18.startPoint())
+SketchLine_19 = Sketch_1.addLine(6.535, 9.187, 6.535, 10.256)
+SketchConstraintCoincidence_20 = Sketch_1.setCoincident(SketchLine_18.endPoint(), SketchLine_19.startPoint())
+SketchLine_20 = Sketch_1.addLine(6.535, 10.256, 6.185, 10.256)
+SketchConstraintCoincidence_21 = Sketch_1.setCoincident(SketchLine_19.endPoint(), SketchLine_20.startPoint())
+SketchLine_21 = Sketch_1.addLine(6.185, 10.256, 4.9175, 10.94235984621998)
+SketchConstraintCoincidence_22 = Sketch_1.setCoincident(SketchLine_20.endPoint(), SketchLine_21.startPoint())
+SketchConstraintHorizontal_7 = Sketch_1.setHorizontal(SketchLine_20.result())
+SketchConstraintVertical_9 = Sketch_1.setVertical(SketchLine_19.result())
+SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_10.result(), SketchLine_18.result(), 45, type = "Supplementary")
+SketchConstraintLength_4 = Sketch_1.setLength(SketchLine_20.result(), 0.35)
+SketchConstraintDistance_9 = Sketch_1.setDistance(SketchLine_19.endPoint(), SketchLine_12.result(), 10.256, True)
+SketchArc_1 = Sketch_1.addArc(0, 2.425, 4.9175, 10.94235984621998, 0, 12.26, False)
+SketchConstraintCoincidence_23 = Sketch_1.setCoincident(SketchLine_11.result(), SketchArc_1.center())
+SketchConstraintCoincidence_24 = Sketch_1.setCoincident(SketchLine_21.endPoint(), SketchArc_1.startPoint())
+SketchConstraintCoincidence_25 = Sketch_1.setCoincident(SketchLine_11.result(), SketchArc_1.endPoint())
+SketchLine_22 = Sketch_1.addLine(6.188, 9.028, 6.082, 9.532)
+SketchConstraintCoincidence_26 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_22.startPoint())
+SketchArc_2 = Sketch_1.addArc(4.79378612024245, 9.263, 6.082, 9.532, 5.676067550792229, 10.23944020672391, False)
+SketchConstraintCoincidence_27 = Sketch_1.setCoincident(SketchLine_22.endPoint(), SketchArc_2.startPoint())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_2.results()[1], 1.316)
+SketchConstraintDistance_10 = Sketch_1.setDistance(SketchArc_2.startPoint(), SketchLine_1.result(), 0.106, True)
+SketchLine_23 = Sketch_1.addLine(5.676067550792229, 10.23944020672391, 5.405090045827156, 10.43837553323928)
+SketchConstraintCoincidence_28 = Sketch_1.setCoincident(SketchArc_2.endPoint(), SketchLine_23.startPoint())
+SketchLine_24 = Sketch_1.addLine(5.405090045827156, 10.43837553323928, 5.126644475052085, 10.62934617154252)
+SketchConstraintCoincidence_29 = Sketch_1.setCoincident(SketchLine_23.endPoint(), SketchLine_24.startPoint())
+SketchLine_25 = Sketch_1.addLine(5.126644475052085, 10.62934617154252, 4.8355, 10.80033167999934)
+SketchConstraintCoincidence_30 = Sketch_1.setCoincident(SketchLine_24.endPoint(), SketchLine_25.startPoint())
+SketchArc_3 = Sketch_1.addArc(0, 2.425, 4.8355, 10.80033167999934, 0, 12.096, False)
+SketchConstraintCoincidence_31 = Sketch_1.setCoincident(SketchLine_11.result(), SketchArc_3.center())
+SketchConstraintCoincidence_32 = Sketch_1.setCoincident(SketchLine_25.endPoint(), SketchArc_3.startPoint())
+SketchConstraintCoincidence_33 = Sketch_1.setCoincident(SketchLine_11.result(), SketchArc_3.endPoint())
+SketchLine_26 = Sketch_1.addLine(0, 12.096, 0, 12.26)
+SketchConstraintCoincidence_34 = Sketch_1.setCoincident(SketchArc_3.endPoint(), SketchLine_26.startPoint())
+SketchConstraintCoincidence_35 = Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_26.endPoint())
+SketchConstraintDistance_11 = Sketch_1.setDistance(SketchArc_1.center(), SketchLine_12.result(), 2.425, True)
+SketchConstraintCoincidence_36 = Sketch_1.setCoincident(SketchArc_3.center(), SketchArc_1.center())
+SketchConstraintLength_5 = Sketch_1.setLength(SketchLine_26.result(), 0.164)
+SketchLine_27 = Sketch_1.addLine(0, 2.425, 4.9175, 10.94235984621998)
+SketchLine_27.setAuxiliary(True)
+SketchConstraintCoincidence_37 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_27.startPoint())
+SketchConstraintCoincidence_38 = Sketch_1.setCoincident(SketchLine_21.endPoint(), SketchLine_27.endPoint())
+SketchConstraintCoincidence_39 = Sketch_1.setCoincident(SketchArc_3.startPoint(), SketchLine_27.result())
+SketchLine_28 = Sketch_1.addLine(0, 2.425, 5.21991026555713, 10.77860263646605)
+SketchLine_28.setAuxiliary(True)
+SketchConstraintCoincidence_40 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_28.startPoint())
+SketchConstraintCoincidence_41 = Sketch_1.setCoincident(SketchLine_28.endPoint(), SketchLine_21.result())
+SketchConstraintCoincidence_42 = Sketch_1.setCoincident(SketchLine_28.result(), SketchLine_25.startPoint())
+SketchLine_29 = Sketch_1.addLine(0, 2.425, 5.523638941362955, 10.61413149862094)
+SketchLine_29.setAuxiliary(True)
+SketchConstraintCoincidence_43 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_29.startPoint())
+SketchConstraintCoincidence_44 = Sketch_1.setCoincident(SketchLine_29.endPoint(), SketchLine_21.result())
+SketchConstraintCoincidence_45 = Sketch_1.setCoincident(SketchLine_29.result(), SketchLine_24.startPoint())
+SketchConstraintDistance_12 = Sketch_1.setDistance(SketchLine_25.startPoint(), SketchLine_28.endPoint(), 0.176, True)
+SketchConstraintDistance_13 = Sketch_1.setDistance(SketchLine_24.startPoint(), SketchLine_29.endPoint(), 0.212, True)
+SketchConstraintAngle_2 = Sketch_1.setAngle(SketchLine_28.result(), SketchLine_11.result(), 32, type = "Direct")
+SketchConstraintAngle_3 = Sketch_1.setAngle(SketchLine_29.result(), SketchLine_11.result(), 34, type = "Direct")
+SketchConstraintAngle_4 = Sketch_1.setAngle(SketchLine_27.result(), SketchLine_11.result(), 30, type = "Direct")
+SketchLine_30 = Sketch_1.addLine(4.79378612024245, 9.263, 5.676067550792229, 10.23944020672391)
+SketchLine_30.setAuxiliary(True)
+SketchConstraintCoincidence_46 = Sketch_1.setCoincident(SketchArc_2.center(), SketchLine_30.startPoint())
+SketchConstraintCoincidence_47 = Sketch_1.setCoincident(SketchArc_2.endPoint(), SketchLine_30.endPoint())
+SketchLine_31 = Sketch_1.addLine(4.79378612024245, 9.263, 4.79378612024245, 10.72808112087839)
+SketchLine_31.setAuxiliary(True)
+SketchConstraintCoincidence_48 = Sketch_1.setCoincident(SketchArc_2.center(), SketchLine_31.startPoint())
+SketchConstraintVertical_10 = Sketch_1.setVertical(SketchLine_31.result())
+SketchConstraintAngle_5 = Sketch_1.setAngle(SketchLine_30.result(), SketchLine_31.result(), 42.1, type = "Direct")
+SketchConstraintDistance_14 = Sketch_1.setDistance(SketchArc_2.center(), SketchLine_12.result(), 9.263, True)
+SketchConstraintCoincidence_49 = Sketch_1.setCoincident(SketchLine_31.endPoint(), SketchLine_27.result())
+SketchConstraintDistance_15 = Sketch_1.setDistance(SketchLine_22.endPoint(), SketchLine_12.result(), 9.532, True)
+SketchConstraintDistance_16 = Sketch_1.setDistance(SketchLine_18.endPoint(), SketchLine_12.result(), 9.187, True)
+SketchConstraintRadius_2 = Sketch_1.setRadius(SketchArc_1.results()[1], 9.835)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("FACE", (3.891, 0, 2.905000000000017))], model.selection("EDGE", "PartSet/OZ"), 360, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOZ"))
+SketchProjection_3 = Sketch_2.addProjection(model.selection("VERTEX_5", (0, 0, 2.425)), False)
+SketchPoint_1 = SketchProjection_3.createdFeature()
+SketchProjection_4 = Sketch_2.addProjection(model.selection("EDGE", (0, 0, 12.178)), False)
+SketchLine_32 = SketchProjection_4.createdFeature()
+SketchArc_4 = Sketch_2.addArc(0, 2.425, 0, 12.178, 5.453808387550563, 10.51060344512907, True)
+SketchArc_4.setAuxiliary(True)
+SketchConstraintCoincidence_50 = Sketch_2.setCoincident(SketchPoint_1.result(), SketchArc_4.center())
+SketchConstraintCoincidence_51 = Sketch_2.setCoincident(SketchLine_32.result(), SketchArc_4.startPoint())
+SketchProjection_5 = Sketch_2.addProjection(model.selection("EDGE", (2.761819470681477, 0, 6.519565749310471)), False)
+SketchLine_33 = SketchProjection_5.createdFeature()
+SketchConstraintCoincidence_52 = Sketch_2.setCoincident(SketchArc_4.endPoint(), SketchLine_33.result())
+SketchConstraintMiddle_3 = Sketch_2.setMiddlePoint(SketchArc_4.startPoint(), SketchLine_32.result())
+SketchLine_34 = Sketch_2.addLine(0, 2.425, 3.282538500974084, 11.74164236673399)
+SketchLine_34.setAuxiliary(True)
+SketchConstraintCoincidence_53 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_1).coordinates(), SketchLine_34.startPoint())
+SketchLine_35 = Sketch_2.addLine(3.282538500974084, 11.74164236673399, 3.199461499025965, 11.50584963625379)
+SketchConstraintCoincidence_54 = Sketch_2.setCoincident(SketchLine_34.endPoint(), SketchLine_35.startPoint())
+SketchConstraintCoincidence_55 = Sketch_2.setCoincident(SketchLine_35.endPoint(), SketchLine_34.result())
+SketchConstraintLength_6 = Sketch_2.setLength(SketchLine_35.result(), 0.25)
+SketchPoint_2 = Sketch_2.addPoint(3.241, 11.6237460014939)
+SketchConstraintCoincidence_56 = Sketch_2.setCoincident(SketchPoint_2.coordinates(), SketchArc_4.results()[1])
+SketchConstraintCoincidence_57 = Sketch_2.setCoincident(SketchPoint_2.coordinates(), SketchLine_35.result())
+SketchProjection_6 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_36 = SketchProjection_6.createdFeature()
+SketchConstraintDistance_17 = Sketch_2.setDistance(SketchPoint_2.coordinates(), SketchLine_36.result(), 3.241, True)
+SketchArc_5 = Sketch_2.addArc(0, 2.425, 3.282538500974084, 11.74164236673399, 4.1398177019328, 11.393656164374, True)
+SketchConstraintCoincidence_58 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_1).coordinates(), SketchArc_5.center())
+SketchConstraintCoincidence_59 = Sketch_2.setCoincident(SketchLine_34.endPoint(), SketchArc_5.startPoint())
+SketchArc_6 = Sketch_2.addArc(0, 2.425, 3.199461499025965, 11.50584963625379, 4.035044020470642, 11.16667053559353, True)
+SketchConstraintCoincidence_60 = Sketch_2.setCoincident(SketchAPI_Line(SketchLine_33).startPoint(), SketchArc_6.center())
+SketchConstraintCoincidence_61 = Sketch_2.setCoincident(SketchLine_35.endPoint(), SketchArc_6.startPoint())
+SketchLine_37 = Sketch_2.addLine(0, 2.425, 3.911506014428326, 11.49555702253677)
+SketchLine_37.setAuxiliary(True)
+SketchConstraintCoincidence_62 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_1).coordinates(), SketchLine_37.startPoint())
+SketchConstraintCoincidence_63 = Sketch_2.setCoincident(SketchLine_37.endPoint(), SketchArc_5.results()[1])
+SketchLine_38 = Sketch_2.addLine(0, 2.425, 4.1398177019328, 11.393656164374)
+SketchLine_38.setAuxiliary(True)
+SketchConstraintCoincidence_64 = Sketch_2.setCoincident(SketchAPI_Point(SketchPoint_1).coordinates(), SketchLine_38.startPoint())
+SketchConstraintCoincidence_65 = Sketch_2.setCoincident(SketchArc_5.endPoint(), SketchLine_38.endPoint())
+SketchConstraintCoincidence_66 = Sketch_2.setCoincident(SketchArc_6.endPoint(), SketchLine_38.result())
+SketchConstraintDistance_18 = Sketch_2.setDistance(SketchLine_35.startPoint(), SketchLine_37.result(), 0.675, True)
+SketchConstraintMiddle_4 = Sketch_2.setMiddlePoint(SketchPoint_2.coordinates(), SketchLine_35.result())
+SketchLine_39 = Sketch_2.addLine(3.911506014428326, 11.49555702253677, 3.81251062025875, 11.26599240868435)
+SketchConstraintCoincidence_67 = Sketch_2.setCoincident(SketchLine_37.endPoint(), SketchLine_39.startPoint())
+SketchConstraintCoincidence_68 = Sketch_2.setCoincident(SketchLine_39.endPoint(), SketchArc_6.results()[1])
+SketchConstraintCoincidence_69 = Sketch_2.setCoincident(SketchLine_39.endPoint(), SketchLine_37.result())
+SketchLine_40 = Sketch_2.addLine(4.1398177019328, 11.393656164374, 4.459277785066647, 11.19096067956113)
+SketchConstraintCoincidence_70 = Sketch_2.setCoincident(SketchArc_5.endPoint(), SketchLine_40.startPoint())
+SketchProjection_7 = Sketch_2.addProjection(model.selection("EDGE_3", (2.545485308583308, 0, 11.92488050155302)), False)
+SketchArc_7 = SketchProjection_7.createdFeature()
+SketchConstraintCoincidence_71 = Sketch_2.setCoincident(SketchLine_40.endPoint(), SketchArc_7.results()[1])
+SketchLine_41 = Sketch_2.addLine(4.459277785066647, 11.19096067956113, 4.390495384356095, 11.04194790978503)
+SketchConstraintCoincidence_72 = Sketch_2.setCoincident(SketchLine_40.endPoint(), SketchLine_41.startPoint())
+SketchProjection_8 = Sketch_2.addProjection(model.selection("EDGE_3", (2.503038985186497, 0, 11.76646866604162)), False)
+SketchArc_8 = SketchProjection_8.createdFeature()
+SketchConstraintCoincidence_73 = Sketch_2.setCoincident(SketchLine_41.endPoint(), SketchArc_8.results()[1])
+SketchLine_42 = Sketch_2.addLine(4.390495384356095, 11.04194790978503, 4.035044020470642, 11.16667053559353)
+SketchConstraintCoincidence_74 = Sketch_2.setCoincident(SketchLine_41.endPoint(), SketchLine_42.startPoint())
+SketchConstraintCoincidence_75 = Sketch_2.setCoincident(SketchLine_42.endPoint(), SketchLine_38.result())
+SketchConstraintCoincidence_76 = Sketch_2.setCoincident(SketchLine_42.endPoint(), SketchArc_6.endPoint())
+SketchConstraintDistance_19 = Sketch_2.setDistance(SketchArc_5.endPoint(), SketchLine_41.result(), 0.375, True)
+SketchConstraintDistance_20 = Sketch_2.setDistance(SketchLine_39.startPoint(), SketchLine_38.result(), 0.25, True)
+SketchConstraintParallel_1 = Sketch_2.setParallel(SketchLine_41.result(), SketchLine_38.result())
+model.do()
+Revolution_2 = model.addRevolution(Part_1_doc, [model.selection("WIRE", (3.862008317343538, 0, 11.38077471561056))], model.selection("EDGE", (3.241000000000025, 0, 11.62374600149389)), 360, 0)
+Revolution_3 = model.addRevolution(Part_1_doc, [model.selection("WIRE", (4.212769702413368, 0, 11.10430922268928))], model.selection("EDGE", (3.241000000000025, 0, 11.62374600149389)), 360, 0)
+Symmetry_1 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", (3.237001631529592, 7.752869270149964e-17, 11.61239765963003)), model.selection("SOLID", (3.223059230627338, 1.806177914907969e-16, 11.57282573597984))], model.selection("FACE", "PartSet/YOZ"), True)
+Cut_1 = model.addCut(Part_1_doc, [model.selection("SOLID", (9.171993615882458e-05, 1.467829010982793e-16, 1.298864381774082))], [model.selection("COMPOUND", (0, 0, 11.60508262087049))])
+Fuse_1 = model.addFuse(Part_1_doc, [model.selection("SOLID", (9.190387067667382e-05, -2.335384201710612e-09, 1.278348504308934)), model.selection("COMPOUND", (0, 0, 11.54608275997197))], removeEdges = True)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/XOY"), 3.6, False)
+Sketch_3 = model.addSketch(Part_1_doc, model.selection("FACE", (0, 0, 3.6)))
+SketchProjection_9 = Sketch_3.addProjection(model.selection("EDGE", (-6.188, 7.578114393123822e-16, -3.45)), True)
+SketchCircle_1 = SketchProjection_9.createdFeature()
+SketchProjection_10 = Sketch_3.addProjection(model.selection("EDGE", (-6.376, 7.808347991363525e-16, -2.25)), True)
+SketchCircle_2 = SketchProjection_10.createdFeature()
+SketchArc_9 = Sketch_3.addArc(0, 0, 6.094, 0, 5.859569950090201, -1.674, True)
+SketchConstraintCoincidence_77 = Sketch_3.setCoincident(SketchAPI_Circle(SketchCircle_1).center(), SketchArc_9.center())
+SketchProjection_11 = Sketch_3.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_43 = SketchProjection_11.createdFeature()
+SketchConstraintCoincidence_78 = Sketch_3.setCoincident(SketchArc_9.startPoint(), SketchLine_43.result())
+SketchPoint_3 = Sketch_3.addPoint(6.188, 0)
+SketchConstraintCoincidence_79 = Sketch_3.setCoincident(SketchPoint_3.coordinates(), SketchLine_43.result())
+SketchConstraintCoincidence_80 = Sketch_3.setCoincident(SketchPoint_3.coordinates(), SketchCircle_1.results()[1])
+SketchConstraintDistance_21 = Sketch_3.setDistance(SketchArc_9.startPoint(), SketchPoint_3.coordinates(), 0.094, True)
+SketchConstraintDistance_22 = Sketch_3.setDistance(SketchArc_9.endPoint(), SketchLine_43.result(), 1.674, True)
+SketchLine_44 = Sketch_3.addLine(5.859569950090201, -1.674, 5.870724657144867, -1.956)
+SketchConstraintCoincidence_81 = Sketch_3.setCoincident(SketchArc_9.endPoint(), SketchLine_44.startPoint())
+SketchConstraintCoincidence_82 = Sketch_3.setCoincident(SketchLine_44.endPoint(), SketchCircle_1.results()[1])
+SketchArc_10 = Sketch_3.addArc(0, 0, 6.47, 0, 6.167249305809295, -1.956, True)
+SketchConstraintCoincidence_83 = Sketch_3.setCoincident(SketchAPI_Circle(SketchCircle_1).center(), SketchArc_10.center())
+SketchConstraintCoincidence_84 = Sketch_3.setCoincident(SketchLine_43.result(), SketchArc_10.startPoint())
+SketchLine_45 = Sketch_3.addLine(6.167249305809295, -1.956, 6.068561608816571, -1.956)
+SketchConstraintCoincidence_85 = Sketch_3.setCoincident(SketchArc_10.endPoint(), SketchLine_45.startPoint())
+SketchConstraintCoincidence_86 = Sketch_3.setCoincident(SketchLine_45.endPoint(), SketchCircle_2.results()[1])
+SketchConstraintHorizontal_8 = Sketch_3.setHorizontal(SketchLine_45.result())
+SketchConstraintCoincidence_87 = Sketch_3.setCoincident(SketchLine_44.endPoint(), SketchLine_45.result())
+SketchConstraintDistance_23 = Sketch_3.setDistance(SketchArc_10.startPoint(), SketchLine_45.result(), 1.956, True)
+SketchPoint_4 = Sketch_3.addPoint(6.376, 0)
+SketchConstraintCoincidence_88 = Sketch_3.setCoincident(SketchPoint_4.coordinates(), SketchCircle_2.results()[1])
+SketchConstraintCoincidence_89 = Sketch_3.setCoincident(SketchPoint_4.coordinates(), SketchLine_43.result())
+SketchConstraintDistance_24 = Sketch_3.setDistance(SketchPoint_4.coordinates(), SketchArc_10.startPoint(), 0.094, True)
+SketchConstraintMirror_1_objects = [SketchArc_9.results()[1], SketchLine_44.result(), SketchArc_10.results()[1], SketchLine_45.result()]
+SketchConstraintMirror_1 = Sketch_3.addMirror(SketchLine_43.result(), SketchConstraintMirror_1_objects)
+[SketchArc_11, SketchLine_46, SketchArc_12, SketchLine_47] = SketchConstraintMirror_1.mirrored()
+model.do()
+Sketch_3.changeFacesOrder([[SketchProjection_9.result(), SketchProjection_9.result(), SketchLine_46.result(), SketchArc_11.results()[1], SketchArc_9.results()[1], SketchLine_44.result()],
+                           [SketchProjection_10.result(), SketchProjection_10.result(), SketchProjection_10.result(), SketchProjection_9.result(), SketchProjection_9.result(), SketchProjection_9.result()],
+                           [SketchProjection_9.result(), SketchLine_44.result(), SketchArc_9.results()[1], SketchArc_11.results()[1], SketchLine_46.result()],
+                           [SketchProjection_10.result(), SketchLine_45.result(), SketchArc_10.results()[1], SketchArc_12.results()[1], SketchLine_47.result(), SketchProjection_10.result()]
+                          ])
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", (6.023784975045098, 0, 3.6)), model.selection("WIRE", (6.393864363926704, 0.9896456414839456, 3.6))], model.selection(), 0, 4.2)
+Sketch_4 = model.addSketch(Part_1_doc, model.standardPlane("YOZ"))
+SketchLine_48 = Sketch_4.addLine(0.75, 2.1, -0.75, 2.1)
+SketchLine_49 = Sketch_4.addLine(-0.75, 2.1, -0.75, 0.3)
+SketchLine_50 = Sketch_4.addLine(-0.75, 0.3, 0.75, 0.3)
+SketchLine_51 = Sketch_4.addLine(0.75, 0.3, 0.75, 2.1)
+SketchConstraintCoincidence_90 = Sketch_4.setCoincident(SketchLine_51.endPoint(), SketchLine_48.startPoint())
+SketchConstraintCoincidence_91 = Sketch_4.setCoincident(SketchLine_48.endPoint(), SketchLine_49.startPoint())
+SketchConstraintCoincidence_92 = Sketch_4.setCoincident(SketchLine_49.endPoint(), SketchLine_50.startPoint())
+SketchConstraintCoincidence_93 = Sketch_4.setCoincident(SketchLine_50.endPoint(), SketchLine_51.startPoint())
+SketchConstraintHorizontal_9 = Sketch_4.setHorizontal(SketchLine_48.result())
+SketchConstraintVertical_11 = Sketch_4.setVertical(SketchLine_49.result())
+SketchConstraintHorizontal_10 = Sketch_4.setHorizontal(SketchLine_50.result())
+SketchConstraintVertical_12 = Sketch_4.setVertical(SketchLine_51.result())
+SketchProjection_12 = Sketch_4.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_52 = SketchProjection_12.createdFeature()
+SketchConstraintDistance_25 = Sketch_4.setDistance(SketchLine_52.result(), SketchLine_50.endPoint(), 0.3, True)
+SketchConstraintDistance_26 = Sketch_4.setDistance(SketchLine_51.endPoint(), SketchLine_52.result(), 2.1, True)
+SketchPoint_5 = Sketch_4.addPoint(0, 2.1)
+SketchConstraintCoincidence_94 = Sketch_4.setCoincident(SketchPoint_5.coordinates(), SketchLine_48.result())
+SketchConstraintMiddle_5 = Sketch_4.setMiddlePoint(SketchLine_48.result(), SketchPoint_5.coordinates())
+SketchProjection_13 = Sketch_4.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_53 = SketchProjection_13.createdFeature()
+SketchConstraintCoincidence_95 = Sketch_4.setCoincident(SketchPoint_5.coordinates(), SketchLine_53.result())
+SketchConstraintDistance_27 = Sketch_4.setDistance(SketchPoint_5.coordinates(), SketchLine_51.endPoint(), 0.75, True)
+model.do()
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("FACE", (0, 0, 1.2))], model.selection(), 10, 0)
+Rotation_1_objects = [model.selection("SOLID", (5, 1.166200656118862e-18, 1.2)), model.selection("SOLID", (6.038396788450424, -2.054057850558325e-15, 1.5)), model.selection("SOLID", (6.316150725419533, -7.758519898162708e-14, 1.5))]
+Rotation_1 = model.addRotation(Part_1_doc, Rotation_1_objects, model.selection("EDGE", "PartSet/OZ"), -90)
+Fuse_2_objects_1 = [model.selection("SOLID", (9.187239971515918e-05, -6.832080613557312e-09, 1.282177819724956)), model.selection("SOLID", (-1.349638054860434e-15, -6.038396788450424, 1.5)), model.selection("SOLID", (-7.680624658573511e-14, -6.316150725419533, 1.5))]
+Fuse_2 = model.addFuse(Part_1_doc, Fuse_2_objects_1)
+Cut_2 = model.addCut(Part_1_doc, [model.selection("SOLID", (9.177890659903279e-05, -0.006341145656800184, 1.28239948002437))], [model.selection("SOLID", (6.393306363619619e-16, -4.999999999999998, 1.2))])
+FusionFaces_1 = model.addFusionFaces(Part_1_doc, model.selection("SOLID", (9.190526314899106e-05, 0.002297421001363739, 1.282512923455053)))
+
+model.end()
+
+model.testNbResults(FusionFaces_1, 1)
+model.testNbSubResults(FusionFaces_1, [0])
+model.testNbSubShapes(FusionFaces_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(FusionFaces_1, GeomAPI_Shape.FACE, [47])
+model.testNbSubShapes(FusionFaces_1, GeomAPI_Shape.EDGE, [202])
+model.testNbSubShapes(FusionFaces_1, GeomAPI_Shape.VERTEX, [404])
+model.testResultsVolumes(FusionFaces_1, [612.7268292882])
+
+assert(model.checkPythonDump())
\ No newline at end of file
diff --git a/src/FeaturesPlugin/Test/Test3137_1.py b/src/FeaturesPlugin/Test/Test3137_1.py
new file mode 100644 (file)
index 0000000..b3fedb2
--- /dev/null
@@ -0,0 +1,65 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [], model.selection(), 20, 0)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(15.15541180270434, 24.41800127226657, 10.26187655283108)
+Extrusion_1.setNestedSketch(Sketch_1)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Extrusion_2 = model.addExtrusion(Part_2_doc, [], model.selection(), 10, 0)
+Sketch_2 = model.addSketch(Part_2_doc, model.defaultPlane("YOZ"))
+SketchLine_1 = Sketch_2.addLine(-19.91316598300577, 19.16479847747027, -37.02832010642763, 19.16479847747027)
+SketchLine_2 = Sketch_2.addLine(-37.02832010642763, 19.16479847747027, -37.02832010642763, 36.27995260089213)
+SketchLine_3 = Sketch_2.addLine(-37.02832010642763, 36.27995260089213, -19.91316598300577, 36.27995260089213)
+SketchLine_4 = Sketch_2.addLine(-19.91316598300577, 36.27995260089213, -19.91316598300577, 19.16479847747027)
+SketchConstraintCoincidence_1 = Sketch_2.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_2.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_2.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_2.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_4.result(), SketchLine_3.result())
+Extrusion_2.setNestedSketch(Sketch_2)
+model.do()
+
+LinearCopy_1 = model.addMultiTranslation(partSet, [model.selection("COMPOUND", "Part_1/"), model.selection("COMPOUND", "Part_2/")], model.selection("EDGE", "OX"), 50, 2)
+LinearCopy_2_objects = [model.selection("COMPOUND", "LinearCopy_1/"), model.selection("COMPOUND", "LinearCopy_1_2/"), model.selection("COMPOUND", "LinearCopy_1_3/"), model.selection("COMPOUND", "LinearCopy_1_4/")]
+LinearCopy_2 = model.addMultiTranslation(partSet, LinearCopy_2_objects, model.selection("EDGE", "OY"), 30, 2, model.selection("EDGE", "OZ"), 25, 2)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(LinearCopy_2, 16)
+model.testNbSubResults(LinearCopy_2, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+model.testNbSubShapes(LinearCopy_2, GeomAPI_Shape.SOLID, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
+model.testNbSubShapes(LinearCopy_2, GeomAPI_Shape.FACE, [3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6])
+model.testNbSubShapes(LinearCopy_2, GeomAPI_Shape.EDGE, [6, 6, 6, 6, 6, 6, 6, 6, 24, 24, 24, 24, 24, 24, 24, 24])
+model.testNbSubShapes(LinearCopy_2, GeomAPI_Shape.VERTEX, [12, 12, 12, 12, 12, 12, 12, 12, 48, 48, 48, 48, 48, 48, 48, 48])
+model.testResultsVolumes(LinearCopy_2, [6616.5780553, 6616.5780553, 6616.5780553, 6616.5780553, 6616.5780553, 6616.5780553, 6616.5780553, 6616.5780553, 2929.285, 2929.285, 2929.285, 2929.285, 2929.285, 2929.285, 2929.285, 2929.285])
diff --git a/src/FeaturesPlugin/Test/Test3137_2.py b/src/FeaturesPlugin/Test/Test3137_2.py
new file mode 100644 (file)
index 0000000..6945ca7
--- /dev/null
@@ -0,0 +1,63 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [], model.selection(), 20, 0)
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(15.15541180270434, 24.41800127226657, 10.26187655283108)
+Extrusion_1.setNestedSketch(Sketch_1)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Extrusion_2 = model.addExtrusion(Part_2_doc, [], model.selection(), 10, 0)
+Sketch_2 = model.addSketch(Part_2_doc, model.defaultPlane("YOZ"))
+SketchLine_1 = Sketch_2.addLine(-19.91316598300577, 19.16479847747027, -37.02832010642763, 19.16479847747027)
+SketchLine_2 = Sketch_2.addLine(-37.02832010642763, 19.16479847747027, -37.02832010642763, 36.27995260089213)
+SketchLine_3 = Sketch_2.addLine(-37.02832010642763, 36.27995260089213, -19.91316598300577, 36.27995260089213)
+SketchLine_4 = Sketch_2.addLine(-19.91316598300577, 36.27995260089213, -19.91316598300577, 19.16479847747027)
+SketchConstraintCoincidence_1 = Sketch_2.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_2 = Sketch_2.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_1 = Sketch_2.setHorizontal(SketchLine_1.result())
+SketchConstraintVertical_1 = Sketch_2.setVertical(SketchLine_2.result())
+SketchConstraintHorizontal_2 = Sketch_2.setHorizontal(SketchLine_3.result())
+SketchConstraintVertical_2 = Sketch_2.setVertical(SketchLine_4.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_4.result(), SketchLine_3.result())
+Extrusion_2.setNestedSketch(Sketch_2)
+model.do()
+
+AngularCopy_1 = model.addMultiRotation(partSet, [model.selection("COMPOUND", "Part_1/"), model.selection("COMPOUND", "Part_2/")], model.selection("EDGE", "OZ"), 90, 2)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(AngularCopy_1, 4)
+model.testNbSubResults(AngularCopy_1, [0, 0, 0, 0])
+model.testNbSubShapes(AngularCopy_1, GeomAPI_Shape.SOLID, [1, 1, 1, 1])
+model.testNbSubShapes(AngularCopy_1, GeomAPI_Shape.FACE, [3, 3, 6, 6])
+model.testNbSubShapes(AngularCopy_1, GeomAPI_Shape.EDGE, [6, 6, 24, 24])
+model.testNbSubShapes(AngularCopy_1, GeomAPI_Shape.VERTEX, [12, 12, 48, 48])
+model.testResultsVolumes(AngularCopy_1, [6616.5780553, 6616.5780553, 2929.285, 2929.285])
diff --git a/src/FeaturesPlugin/Test/TestCopyFeature.py b/src/FeaturesPlugin/Test/TestCopyFeature.py
new file mode 100644 (file)
index 0000000..17f99d1
--- /dev/null
@@ -0,0 +1,48 @@
+# 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
+#
+
+# Checks selection of the whole featurte and move to the end of the group created on results of this feature..
+
+from salome.shaper import model
+
+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"))
+SketchCircle_1 = Sketch_1.addCircle(11.02869497636673, 9.8764247475525, 3.312248077480665)
+SketchCircle_2 = Sketch_1.addCircle(4.278198729238611, 4.677840612715367, 1.794922837237287)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1")], model.selection(), 10, 0)
+Group_1 = model.addGroup(Part_1_doc, "Faces", [model.selection("SOLID", "Extrusion_1_1"), model.selection("SOLID", "Extrusion_1_2")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPOUND", "all-in-Extrusion_1")], 1)
+model.do()
+# move the group feature to the end - through copy of the whole feature
+Part_1_doc.moveFeature(Group_1.feature(), Copy_1.feature())
+model.end()
+
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 4) # two original solids plus two copies
+assert(selectionList.value(3).namingName() == "Extrusion_1_1_1_1")
+assert(selectionList.value(2).namingName() == "Extrusion_1_1_1_2")
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyFeatureMoveGroupOfFeature.py b/src/FeaturesPlugin/Test/TestCopyFeatureMoveGroupOfFeature.py
new file mode 100644 (file)
index 0000000..0d8eb2d
--- /dev/null
@@ -0,0 +1,48 @@
+# 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
+#
+
+# Checks selection of the whole featurte and move to the end of the group created on this feature..
+
+from salome.shaper import model
+
+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"))
+SketchCircle_1 = Sketch_1.addCircle(11.02869497636673, 9.8764247475525, 3.312248077480665)
+SketchCircle_2 = Sketch_1.addCircle(4.278198729238611, 4.677840612715367, 1.794922837237287)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1")], model.selection(), 10, 0)
+Group_1 = model.addGroup(Part_1_doc, "Faces", [model.selection("COMPOUND", "all-in-Extrusion_1")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPOUND", "all-in-Extrusion_1")], 1)
+model.do()
+# move the group feature to the end - through copy of the whole feature
+Part_1_doc.moveFeature(Group_1.feature(), Copy_1.feature())
+model.end()
+
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 2) # two: original feature and the copy-feature
+assert(selectionList.value(0).namingName() == "all-in-Extrusion_1")
+assert(selectionList.value(1).namingName() == "all-in-Copy_1")
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyMoveResult.py b/src/FeaturesPlugin/Test/TestCopyMoveResult.py
new file mode 100644 (file)
index 0000000..cebb307
--- /dev/null
@@ -0,0 +1,67 @@
+# 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
+#
+
+# Checks selection of the whole shape and move to the end of the simple copy wihtout the
+# next modifications applied.
+
+from salome.shaper import model
+
+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(38.31034482758622, 31.49775664633739, 7.836206896551726, 31.49775664633739)
+SketchLine_2 = Sketch_1.addLine(7.836206896551726, 31.49775664633739, 7.836206896551726, 8.984209848307833)
+SketchLine_3 = Sketch_1.addLine(7.836206896551726, 8.984209848307833, 38.31034482758622, 8.984209848307833)
+SketchLine_4 = Sketch_1.addLine(38.31034482758622, 8.984209848307833, 38.31034482758622, 31.49775664633739)
+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())
+SketchLine_5 = Sketch_1.addLine(21.64285714285715, 31.4977566463374, 25.0012315270936, 8.984209848307833)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.startPoint(), SketchLine_1.result())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_3.result())
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_5r"), model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_5f-SketchLine_3f-SketchLine_4f")], model.selection(), 7, 0)
+Extrusion_1.result().setName("Origin")
+Group_1 = model.addGroup(Part_1_doc, "Solids", [model.selection("SOLID", "Extrusion_1_1_1")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPSOLID", "Origin")], 1)
+Copy_1.result().setName("Origin_1")
+Fillet_1 = model.addFillet(Part_1_doc, [model.selection("EDGE", "[Origin_1_1/Copy_3][Origin_1_1/Copy_7]"), model.selection("EDGE", "[Origin_1_1/Copy_12][Origin_1_1/Copy_14]")], 2)
+Fillet_1.result().setName("CopyCompound")
+model.do()
+# move the group feature to the end - through copy and fillet on this copy (to distinguish the origin and the copy)
+Part_1_doc.moveFeature(Group_1.feature(), Fillet_1.feature())
+model.end()
+
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 2) # still the same solid + the copied and filleted
+
+assert(selectionList.value(0).namingName() == "Extrusion_1_1_1")
+assert(selectionList.value(1).namingName() == "Fillet_1_1_1")
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyMoveSubShapes.py b/src/FeaturesPlugin/Test/TestCopyMoveSubShapes.py
new file mode 100644 (file)
index 0000000..f2d78e5
--- /dev/null
@@ -0,0 +1,57 @@
+# 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
+#
+
+# Checks selection of the sub-shapes move to the end with combination of 2 Copy features in history
+
+from salome.shaper import model
+from ModelAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Group_1 = model.addGroup(Part_1_doc, "Edges", [model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]")])
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("FACE", "Box_1_1/Front")], 2)
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), 0, 5, [model.selection("SOLID", "Box_1_1")])
+Sketch_1 = model.addSketch(Part_1_doc, model.selection("FACE", "Box_1_1/Front"))
+SketchCircle_1 = Sketch_1.addCircle(9.650212071680357, 9.344990586582618, 1.565166813054581)
+ExtrusionCut_1.setNestedSketch(Sketch_1)
+ExtrusionCut_1.result().setColor(225, 0, 0)
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Box_1_1_1"))
+SketchCircle_2 = Sketch_2.addCircle(0.7603686814133139, 9.06793355634084, 1.630854194501576)
+model.do()
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_2/Face-SketchCircle_2_2r")])
+Cut_1 = model.addCut(Part_1_doc, [model.selection("FACE", "Box_1_1_1")], [model.selection("FACE", "Face_1_1")], keepSubResults = True)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("FACE", "Box_1_1_2")], model.selection("EDGE", "PartSet/OX"), 5)
+Copy_2 = model.addCopy(Part_1_doc, [model.selection("FACE", "Box_1_1_2")], 2)
+Rotation_1 = model.addRotation(Part_1_doc, [model.selection("FACE", "Box_1_1_2_1")], model.selection("EDGE", "PartSet/OZ"), 10)
+Rotation_2 = model.addRotation(Part_1_doc, [model.selection("FACE", "Box_1_1_2_2")], model.selection("EDGE", "PartSet/OZ"), 20)
+model.do()
+# move the group feature to the end - through 2 copies and many modifications of the selected edge
+Part_1_doc.moveFeature(Group_1.feature(), Rotation_2.feature())
+model.end()
+
+# result is 5 edges: cut, translation, 2 rotations, extrusion cut
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Group_1.feature()))
+selectionList = Group_1.feature().selectionList("group_list")
+assert(selectionList.size() == 5)
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyNames.py b/src/FeaturesPlugin/Test/TestCopyNames.py
new file mode 100644 (file)
index 0000000..eac569d
--- /dev/null
@@ -0,0 +1,63 @@
+# 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
+#
+
+# Checks that the Copy feature produces correct names, same as in the description #3109
+
+from salome.shaper import model
+
+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(-11.9408866995074, 14.67733990147784, -29.35467980295567, 14.67733990147784)
+SketchLine_2 = Sketch_1.addLine(-29.35467980295567, 14.67733990147784, -29.35467980295567, -7.960591133004924)
+SketchLine_3 = Sketch_1.addLine(-29.35467980295567, -7.960591133004924, -11.9408866995074, -7.960591133004924)
+SketchLine_4 = Sketch_1.addLine(-11.9408866995074, -7.960591133004924, -11.9408866995074, 14.67733990147784)
+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())
+SketchCircle_1 = Sketch_1.addCircle(14.92610837438425, 16.04556650246306, 6.602917012013241)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f")], model.selection(), 10, 0)
+Extrusion_1.result().setName("Box")
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2f")], model.selection(), 10, 0)
+Extrusion_2.result().setName("Cylinder")
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("SOLID", "Box"), model.selection("SOLID", "Cylinder")], 3)
+model.end()
+
+assert(Copy_1.feature().results().size() == 6)
+
+assert(Copy_1.feature().results()[0].data().name() == "Box_1")
+assert(Copy_1.feature().results()[1].data().name() == "Cylinder_1")
+assert(Copy_1.feature().results()[2].data().name() == "Box_2")
+assert(Copy_1.feature().results()[3].data().name() == "Cylinder_2")
+assert(Copy_1.feature().results()[4].data().name() == "Box_3")
+assert(Copy_1.feature().results()[5].data().name() == "Cylinder_3")
+
+model.begin()
+model.testHaveNamingSubshapes(Copy_1, model, Part_1_doc)
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopySubShapes.py b/src/FeaturesPlugin/Test/TestCopySubShapes.py
new file mode 100644 (file)
index 0000000..f8aedf2
--- /dev/null
@@ -0,0 +1,58 @@
+# 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
+#
+
+# Checks that the Copy feature produces correct results and names if sub-shapes of the same shape are selected
+
+from salome.shaper import model
+
+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(-25.99630541871921, 19.52832512315271, -16.66748768472907, 29.72783251231528)
+SketchLine_2 = Sketch_1.addLine(-16.66748768472907, 29.72783251231528, -12.4384236453202, 17.66256157635469)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(-12.4384236453202, 17.66256157635469, -25.99630541871921, 19.52832512315271)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 10, 0)
+Extrusion_1.result().setName("Prism")
+Copy_1_objects = [model.selection("FACE", "Prism/Generated_Face&Sketch_1/SketchLine_3"), model.selection("FACE", "Prism/Generated_Face&Sketch_1/SketchLine_2"), model.selection("EDGE", "[Prism/Generated_Face&Sketch_1/SketchLine_1][Prism/To_Face]")]
+Copy_1 = model.addCopy(Part_1_doc, Copy_1_objects, 2)
+Copy_1.result().setName("Prism_1")
+model.end()
+
+assert(Copy_1.feature().results().size() == 6)
+
+for index in range(6):
+  # name is just incremented
+  assert(Copy_1.feature().results()[index].data().name() == "Prism_" + str(index + 1))
+  # type of the shape corresponds to selection: 2 faces then edge
+  if index%3 == 2:
+    assert(Copy_1.feature().results()[index].shape().shapeTypeStr() == "EDGE")
+  else:
+    assert(Copy_1.feature().results()[index].shape().shapeTypeStr() == "FACE")
+
+model.begin()
+model.testHaveNamingSubshapes(Copy_1, model, Part_1_doc)
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestCopyWholeFeature.py b/src/FeaturesPlugin/Test/TestCopyWholeFeature.py
new file mode 100644 (file)
index 0000000..0a9a884
--- /dev/null
@@ -0,0 +1,73 @@
+# 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
+#
+
+# Checks that the Copy feature produces correct results and names if the whole result and feature are copied.
+
+from salome.shaper import model
+
+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(-19.47177806477657, -9.723714972297476, -32.14322074518937, 18.42485654704614)
+SketchLine_2 = Sketch_1.addLine(-32.14322074518937, 18.42485654704614, -13.96909823036938, 5.527027954588926)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(-13.96909823036938, 5.527027954588926, -19.47177806477657, -9.723714972297476)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_3.endPoint())
+SketchLine_4 = Sketch_1.addLine(-19.47177806477657, -9.723714972297476, -38.20802856635906, 4.439747463237282)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(-38.20802856635906, 4.439747463237282, -32.14322074518937, 18.42485654704614)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_5.endPoint())
+SketchCircle_1 = Sketch_1.addCircle(15.45187411494798, 10.57784256870842, 6.036432809751617)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_5r-SketchLine_4r"), model.selection("FACE", "Sketch_1/Face-SketchLine_3r-SketchLine_2r-SketchLine_1r")], model.selection(), 10, 0)
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("WIRE", "Sketch_1/Face-SketchCircle_1_2f_wire")], model.selection(), 10, 0)
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("COMPSOLID", "Extrusion_1_1"), model.selection("COMPOUND", "all-in-Extrusion_2")], 1)
+model.end()
+
+assert(Copy_1.feature().results().size() == 2)
+
+from ModelAPI import *
+assert(Copy_1.feature().results()[0].data().name() == "Extrusion_1_1_1")
+assert(modelAPI_ResultBody(Copy_1.feature().results()[0]).subResult(0).data().name() == "Extrusion_1_1_1_1")
+assert(modelAPI_ResultBody(Copy_1.feature().results()[0]).subResult(1).data().name() == "Extrusion_1_1_1_2")
+assert(Copy_1.feature().results()[1].data().name() == "Extrusion_2_1_1")
+
+# Check that copy of the whole feature that contains several results produce compound of all results
+model.begin()
+Extrusion_3 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1")], model.selection(), 10, 0)
+Copy_2 = model.addCopy(Part_1_doc, [model.selection("COMPOUND", "all-in-Extrusion_3")], 1)
+model.end()
+
+assert(Copy_2.feature().results()[0].data().name() == "Extrusion_3_1_1")
+assert(Copy_2.feature().results()[0].shape().shapeTypeStr() == "COMPOUND")
+aSub1 = modelAPI_ResultBody(Copy_2.feature().results()[0]).subResult(0)
+assert(aSub1.shape().shapeTypeStr() == "COMPSOLID")
+aSub2 = modelAPI_ResultBody(Copy_2.feature().results()[0]).subResult(1)
+assert(aSub2.shape().shapeTypeStr() == "SOLID")
+
+model.begin()
+model.testHaveNamingSubshapes(Copy_1, model, Part_1_doc)
+model.testHaveNamingSubshapes(Copy_2, model, Part_1_doc)
+model.end()
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_ErrorMsg.py b/src/FeaturesPlugin/Test/TestDefeaturing_ErrorMsg.py
new file mode 100644 (file)
index 0000000..387605f
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+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)
+
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, [model.selection("FACE", "Box_1_1/Top")])
+assert(Defeaturing_1.feature().error() != "")
+
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Box_2 = model.addBox(Part_2_doc, 10, 10, 10)
+ExtrusionCut_1 = model.addExtrusionCut(Part_2_doc, [], model.selection(), [model.selection("SOLID", "Box_1_1")])
+Sketch_1 = model.addSketch(Part_2_doc, model.selection("FACE", "Box_1_1/Top"))
+SketchCircle_1 = Sketch_1.addCircle(4, 5.137343601256935, 3)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 3)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"), False)
+SketchLine_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchCircle_1.center(), SketchLine_1.result(), 4, True)
+ExtrusionCut_1.setNestedSketch(Sketch_1)
+Plane_4 = model.addPlane(Part_2_doc, model.selection("FACE", "Box_1_1/Back"), model.selection("FACE", "Box_1_1/Front"))
+Partition_1 = model.addPartition(Part_2_doc, [model.selection("SOLID", "ExtrusionCut_1_1"), model.selection("FACE", "Plane_1")], keepSubResults = True)
+
+Defeaturing_2 = model.addDefeaturing(Part_2_doc, [model.selection("FACE", "Partition_1_1_1/Modified_Face&Sketch_1/SketchCircle_1_2")])
+assert(Defeaturing_2.feature().error() != "")
+
+model.end()
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_OnCompound.py b/src/FeaturesPlugin/Test/TestDefeaturing_OnCompound.py
new file mode 100644 (file)
index 0000000..ce66d04
--- /dev/null
@@ -0,0 +1,47 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = model.addBox(Part_1_doc, 20, 20, 10)
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Cylinder_1_1")], 10, 10, 0)
+Cylinder_2 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Cut_1 = model.addCut(Part_1_doc, [model.selection("SOLID", "Box_1_1")], [model.selection("SOLID", "Translation_1_1"), model.selection("SOLID", "Cylinder_2_1")], keepSubResults = True)
+LinearCopy_1 = model.addMultiTranslation(Part_1_doc, [model.selection("SOLID", "Cut_1_1")], model.selection("EDGE", "PartSet/OX"), 30, 2, model.selection("EDGE", "PartSet/OY"), 30, 2)
+Defeaturing_1_objects = [model.selection("FACE", "LinearCopy_1_1_1/MF:Translated&Cylinder_2_1/Face_1"), model.selection("FACE", "LinearCopy_1_1_4/MF:Translated&Cylinder_1_1/Face_1"), model.selection("FACE", "LinearCopy_1_1_3/MF:Translated&Cylinder_2_1/Face_1"), model.selection("FACE", "LinearCopy_1_1_3/MF:Translated&Cylinder_1_1/Face_1")]
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, Defeaturing_1_objects)
+model.testHaveNamingSubshapes(Defeaturing_1, model, Part_1_doc)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Defeaturing_1, 1)
+model.testNbSubResults(Defeaturing_1, [4])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.SOLID, [4])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.FACE, [28])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.EDGE, [120])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.VERTEX, [240])
+model.testResultsVolumes(Defeaturing_1, [14036.50459153])
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid1.py b/src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid1.py
new file mode 100644 (file)
index 0000000..a6807c5
--- /dev/null
@@ -0,0 +1,45 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Cut_1 = model.addCut(Part_1_doc, [model.selection("SOLID", "Box_1_1")], [model.selection("SOLID", "Cylinder_1_1")], keepSubResults = True)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "Cut_1_1/Modified_Face&Box_1_1/Back"), model.selection("FACE", "Box_1_1/Front"))
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "Cut_1_1"), model.selection("FACE", "Plane_1")], keepSubResults = True)
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, [model.selection("FACE", "Cut_1_1/Modified_Face&Cylinder_1_1/Face_1")])
+model.testHaveNamingSubshapes(Defeaturing_1, model, Part_1_doc)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Defeaturing_1, 1)
+model.testNbSubResults(Defeaturing_1, [2])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.FACE, [12])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.EDGE, [48])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.VERTEX, [96])
+model.testResultsVolumes(Defeaturing_1, [1000])
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid2.py b/src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid2.py
new file mode 100644 (file)
index 0000000..6840119
--- /dev/null
@@ -0,0 +1,52 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+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)
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), [model.selection("SOLID", "Box_1_1")])
+Sketch_1 = model.addSketch(Part_1_doc, model.selection("FACE", "Box_1_1/Top"))
+SketchCircle_1 = Sketch_1.addCircle(4, 5.137343601256935, 3)
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchCircle_1.results()[1], 3)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "[Box_1_1/Back][Box_1_1/Top]"), False)
+SketchLine_1 = SketchProjection_1.createdFeature()
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchCircle_1.center(), SketchLine_1.result(), 4, True)
+ExtrusionCut_1.setNestedSketch(Sketch_1)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "Box_1_1/Back"), model.selection("FACE", "Box_1_1/Front"))
+Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "ExtrusionCut_1_1"), model.selection("FACE", "Plane_1")], keepSubResults = True)
+Defeaturing_1_objects = [model.selection("FACE", "Partition_1_1_2/Modified_Face&Sketch_1/SketchCircle_1_2&weak_name_2"), model.selection("FACE", "Partition_1_1_2/Modified_Face&Sketch_1/SketchCircle_1_2&weak_name_1"), model.selection("FACE", "Partition_1_1_1/Modified_Face&Sketch_1/SketchCircle_1_2")]
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, Defeaturing_1_objects)
+model.testHaveNamingSubshapes(Defeaturing_1, model, Part_1_doc)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Defeaturing_1, 1)
+model.testNbSubResults(Defeaturing_1, [2])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.FACE, [12])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.EDGE, [48])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.VERTEX, [96])
+model.testResultsVolumes(Defeaturing_1, [1000])
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid3.py b/src/FeaturesPlugin/Test/TestDefeaturing_OnCompsolid3.py
new file mode 100644 (file)
index 0000000..c63df13
--- /dev/null
@@ -0,0 +1,85 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from SketchAPI import *
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(5, 1.355252715607035e-20, 20, -1.336382355046098e-51)
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchLine_2 = Sketch_1.addLine(20, -1.336382355046098e-51, 20, 5.85786437626905)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchLine_3 = Sketch_1.addLine(20, 5.85786437626905, 15.85786437626905, 10)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(15.85786437626905, 10, 20, 14.14213562373095)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(20, 14.14213562373095, 20, 20)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchLine_6 = Sketch_1.addLine(20, 20, 0, 20)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_6.result())
+SketchLine_7 = Sketch_1.addLine(0, 20, 0, 5)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_7.result())
+SketchConstraintVertical_2.setName("SketchConstraintVertical_3")
+SketchArc_1 = Sketch_1.addArc(0, 0, 5, 1.355252715607035e-20, 0, 5, False)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchArc_1.startPoint())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchArc_1.endPoint())
+SketchConstraintCollinear_1 = Sketch_1.setCollinear(SketchLine_2.result(), SketchLine_5.result())
+SketchConstraintPerpendicular_1 = Sketch_1.setPerpendicular(SketchLine_3.result(), SketchLine_4.result())
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_1.result())
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchArc_1.center(), SketchLine_7.result())
+SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchArc_1.center(), SketchAPI_Point(SketchPoint_1).coordinates())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchArc_1.center(), SketchLine_1.endPoint(), 20, True)
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchArc_1.center(), SketchLine_7.startPoint(), 20, True)
+SketchLine_8 = Sketch_1.addLine(10, 0, 10, 20)
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_8.startPoint(), SketchLine_1.result())
+SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_6.result())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintVertical_3.setName("SketchConstraintVertical_4")
+SketchConstraintMiddle_1 = Sketch_1.setMiddlePoint(SketchLine_8.endPoint(), SketchLine_6.result())
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_5.result())
+SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_4.result())
+SketchConstraintRadius_1 = Sketch_1.setRadius(SketchArc_1.results()[1], 5)
+SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_3.result())
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], model.selection(), 10, 0)
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, [model.selection("FACE", "Extrusion_1_1_2/Generated_Face&Sketch_1/SketchArc_1_2"), model.selection("FACE", "Extrusion_1_1_1/Generated_Face&Sketch_1/SketchLine_3")])
+model.testHaveNamingSubshapes(Defeaturing_1, model, Part_1_doc)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Defeaturing_1, 1)
+model.testNbSubResults(Defeaturing_1, [2])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.SOLID, [2])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.FACE, [14])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.EDGE, [60])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.VERTEX, [120])
+model.testResultsVolumes(Defeaturing_1, [4000])
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_OnSolid1.py b/src/FeaturesPlugin/Test/TestDefeaturing_OnSolid1.py
new file mode 100644 (file)
index 0000000..8feef55
--- /dev/null
@@ -0,0 +1,43 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Box_1 = model.addBox(Part_1_doc, 10, 10, 10)
+Cylinder_1 = model.addCylinder(Part_1_doc, model.selection("VERTEX", "PartSet/Origin"), model.selection("EDGE", "PartSet/OZ"), 5, 10)
+Cut_1 = model.addCut(Part_1_doc, [model.selection("SOLID", "Box_1_1")], [model.selection("SOLID", "Cylinder_1_1")], keepSubResults = True)
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, [model.selection("FACE", "Cut_1_1/Modified_Face&Cylinder_1_1/Face_1")])
+model.testHaveNamingSubshapes(Defeaturing_1, model, Part_1_doc)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Defeaturing_1, 1)
+model.testNbSubResults(Defeaturing_1, [0])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.FACE, [6])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.EDGE, [24])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.VERTEX, [48])
+model.testResultsVolumes(Defeaturing_1, [1000])
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_OnSolid2.py b/src/FeaturesPlugin/Test/TestDefeaturing_OnSolid2.py
new file mode 100644 (file)
index 0000000..a7d7c5d
--- /dev/null
@@ -0,0 +1,95 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+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(10.19197853506727, -21.07109716039953, 30.19197853506727, -21.07109716039953)
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchLine_2 = Sketch_1.addLine(30.19197853506727, -21.07109716039953, 30.19197853506727, -6.071097160399531)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchLine_3 = Sketch_1.addLine(30.19197853506727, -6.071097160399531, 20.19197853506727, -6.071097160399531)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchLine_4 = Sketch_1.addLine(20.19197853506727, -6.071097160399531, 20.19197853506727, 8.928902839600468)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchLine_5 = Sketch_1.addLine(20.19197853506727, 8.928902839600468, 30.19197853506727, 8.928902839600468)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchLine_6 = Sketch_1.addLine(30.19197853506727, 8.928902839600468, 30.19197853506727, 18.92890283960047)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(30.19197853506727, 18.92890283960047, 10.19197853506727, 18.92890283960047)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_7.result())
+SketchLine_8 = Sketch_1.addLine(10.19197853506727, 18.92890283960047, 10.19197853506727, -21.07109716039953)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_8.endPoint())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintVertical_3.setName("SketchConstraintVertical_4")
+SketchConstraintCollinear_1 = Sketch_1.setCollinear(SketchLine_2.result(), SketchLine_6.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 20)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_8.result(), 40)
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_2.result(), 15)
+SketchConstraintLength_4 = Sketch_1.setLength(SketchLine_6.result(), 10)
+SketchConstraintLength_5 = Sketch_1.setLength(SketchLine_3.result(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f-SketchLine_7f-SketchLine_8f")], model.selection(), 10, 0)
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_3"), 0, model.selection(), 0, [model.selection("SOLID", "Extrusion_1_1")])
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_1"))
+SketchLine_9 = Sketch_2.addLine(30.19197853506727, 5, 25.19197853506727, 5)
+SketchLine_10 = Sketch_2.addLine(25.19197853506727, 5, 25.19197853506727, 10)
+SketchLine_11 = Sketch_2.addLine(25.19197853506727, 10, 30.19197853506727, 10)
+SketchLine_12 = Sketch_2.addLine(30.19197853506727, 10, 30.19197853506727, 5)
+SketchConstraintCoincidence_9 = Sketch_2.setCoincident(SketchLine_12.endPoint(), SketchLine_9.startPoint())
+SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintCoincidence_11 = Sketch_2.setCoincident(SketchLine_10.endPoint(), SketchLine_11.startPoint())
+SketchConstraintCoincidence_12 = Sketch_2.setCoincident(SketchLine_11.endPoint(), SketchLine_12.startPoint())
+SketchConstraintHorizontal_5 = Sketch_2.setHorizontal(SketchLine_9.result())
+SketchConstraintVertical_4 = Sketch_2.setVertical(SketchLine_10.result())
+SketchConstraintVertical_4.setName("SketchConstraintVertical_5")
+SketchConstraintHorizontal_6 = Sketch_2.setHorizontal(SketchLine_11.result())
+SketchConstraintVertical_5 = Sketch_2.setVertical(SketchLine_12.result())
+SketchConstraintVertical_5.setName("SketchConstraintVertical_6")
+SketchProjection_1 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_1][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_2][Extrusion_1_1/To_Face]"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_13 = Sketch_2.setCoincident(SketchLine_11.endPoint(), SketchPoint_1.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_12.result(), SketchLine_11.result())
+SketchConstraintLength_6 = Sketch_2.setLength(SketchLine_12.result(), 5)
+ExtrusionCut_1.setNestedSketch(Sketch_2)
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, [model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_4"), model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_5")])
+model.testHaveNamingSubshapes(Defeaturing_1, model, Part_1_doc)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Defeaturing_1, 1)
+model.testNbSubResults(Defeaturing_1, [0])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.FACE, [9])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.EDGE, [42])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.VERTEX, [84])
+model.testResultsVolumes(Defeaturing_1, [7625])
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestDefeaturing_OnSolid3.py b/src/FeaturesPlugin/Test/TestDefeaturing_OnSolid3.py
new file mode 100644 (file)
index 0000000..e564522
--- /dev/null
@@ -0,0 +1,95 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+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(10.19197853506727, -21.07109716039953, 30.19197853506727, -21.07109716039953)
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchLine_2 = Sketch_1.addLine(30.19197853506727, -21.07109716039953, 30.19197853506727, -6.071097160399531)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_2.result())
+SketchLine_3 = Sketch_1.addLine(30.19197853506727, -6.071097160399531, 20.19197853506727, -6.071097160399531)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_3.result())
+SketchLine_4 = Sketch_1.addLine(20.19197853506727, -6.071097160399531, 20.19197853506727, 8.928902839600468)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_4.result())
+SketchLine_5 = Sketch_1.addLine(20.19197853506727, 8.928902839600468, 30.19197853506727, 8.928902839600468)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_5.result())
+SketchLine_6 = Sketch_1.addLine(30.19197853506727, 8.928902839600468, 30.19197853506727, 18.92890283960047)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(30.19197853506727, 18.92890283960047, 10.19197853506727, 18.92890283960047)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_7.result())
+SketchLine_8 = Sketch_1.addLine(10.19197853506727, 18.92890283960047, 10.19197853506727, -21.07109716039953)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_8.endPoint())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_8.result())
+SketchConstraintVertical_3.setName("SketchConstraintVertical_4")
+SketchConstraintCollinear_1 = Sketch_1.setCollinear(SketchLine_2.result(), SketchLine_6.result())
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 20)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_8.result(), 40)
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_2.result(), 15)
+SketchConstraintLength_4 = Sketch_1.setLength(SketchLine_6.result(), 10)
+SketchConstraintLength_5 = Sketch_1.setLength(SketchLine_3.result(), 10)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_4f-SketchLine_5f-SketchLine_6f-SketchLine_7f-SketchLine_8f")], model.selection(), 10, 0)
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [], model.selection(), model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_3"), 0, model.selection(), 0, [model.selection("SOLID", "Extrusion_1_1")])
+Sketch_2 = model.addSketch(Part_1_doc, model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_1"))
+SketchLine_9 = Sketch_2.addLine(30.19197853506727, 5, 25.19197853506727, 5)
+SketchLine_10 = Sketch_2.addLine(25.19197853506727, 5, 25.19197853506727, 10)
+SketchLine_11 = Sketch_2.addLine(25.19197853506727, 10, 30.19197853506727, 10)
+SketchLine_12 = Sketch_2.addLine(30.19197853506727, 10, 30.19197853506727, 5)
+SketchConstraintCoincidence_9 = Sketch_2.setCoincident(SketchLine_12.endPoint(), SketchLine_9.startPoint())
+SketchConstraintCoincidence_10 = Sketch_2.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintCoincidence_11 = Sketch_2.setCoincident(SketchLine_10.endPoint(), SketchLine_11.startPoint())
+SketchConstraintCoincidence_12 = Sketch_2.setCoincident(SketchLine_11.endPoint(), SketchLine_12.startPoint())
+SketchConstraintHorizontal_5 = Sketch_2.setHorizontal(SketchLine_9.result())
+SketchConstraintVertical_4 = Sketch_2.setVertical(SketchLine_10.result())
+SketchConstraintVertical_4.setName("SketchConstraintVertical_5")
+SketchConstraintHorizontal_6 = Sketch_2.setHorizontal(SketchLine_11.result())
+SketchConstraintVertical_5 = Sketch_2.setVertical(SketchLine_12.result())
+SketchConstraintVertical_5.setName("SketchConstraintVertical_6")
+SketchProjection_1 = Sketch_2.addProjection(model.selection("VERTEX", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_1][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_2][Extrusion_1_1/To_Face]"), False)
+SketchPoint_1 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_13 = Sketch_2.setCoincident(SketchLine_11.endPoint(), SketchPoint_1.result())
+SketchConstraintEqual_1 = Sketch_2.setEqual(SketchLine_12.result(), SketchLine_11.result())
+SketchConstraintLength_6 = Sketch_2.setLength(SketchLine_12.result(), 5)
+ExtrusionCut_1.setNestedSketch(Sketch_2)
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, [model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_10"), model.selection("FACE", "ExtrusionCut_1_1/Generated_Face&Sketch_2/SketchLine_9")])
+model.testHaveNamingSubshapes(Defeaturing_1, model, Part_1_doc)
+model.end()
+
+from GeomAPI import *
+
+model.testNbResults(Defeaturing_1, 1)
+model.testNbSubResults(Defeaturing_1, [0])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.FACE, [10])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.EDGE, [48])
+model.testNbSubShapes(Defeaturing_1, GeomAPI_Shape.VERTEX, [96])
+model.testResultsVolumes(Defeaturing_1, [6500])
+
+assert(model.checkPythonDump())
diff --git a/src/FeaturesPlugin/Test/TestImportResult.py b/src/FeaturesPlugin/Test/TestImportResult.py
new file mode 100644 (file)
index 0000000..bd6a0c0
--- /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
+#
+
+from salome.shaper import model
+from ModelAPI import *
+from GeomAPI import *
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(15, 13, 11)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2r")], model.selection(), 10, 0)
+model.do()
+
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+Sketch_2 = model.addSketch(Part_2_doc, model.defaultPlane("XOZ"))
+SketchCircle_2 = Sketch_2.addCircle(-13, -9, 5)
+model.do()
+Extrusion_2 = model.addExtrusion(Part_2_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2r")], model.selection(), 10, 0)
+ImportResult_1 = model.addImportResult(Part_2_doc, [model.selection("SOLID", "Part_1/Extrusion_1_1")])
+model.do()
+
+Part_3 = model.addPart(partSet)
+Part_3_doc = Part_3.document()
+Extrusion_3 = model.addExtrusion(Part_3_doc, [], model.selection(), 1, 0)
+Sketch_3 = model.addSketch(Part_3_doc, model.defaultPlane("XOZ"))
+SketchCircle_3 = Sketch_3.addCircle(-4, 2, 5)
+Extrusion_3.setNestedSketch(Sketch_3)
+model.do()
+
+model.end()
+
+# check Part_2 is ok and contains 2 solids in result
+assert(Part_2.feature().results().size() == 1)
+aPartShape = Part_2.feature().firstResult().shape()
+assert(aPartShape.shapeTypeStr() == "COMPOUND")
+aShapeExplorer = GeomAPI_ShapeExplorer(aPartShape, GeomAPI_Shape.SOLID)
+assert(aShapeExplorer.more())
+aShapeExplorer.next()
+assert(aShapeExplorer.more())
+
+# check that selection of this part and the lower part is impossible (by validator)
+model.begin()
+aSel = ImportResult_1.feature().selectionList("objects").value(0)
+aSel.selectSubShape("SOLID", "Part_2/Extrusion_1_1")
+model.end()
+aFactory = ModelAPI_Session.get().validators()
+assert(not aFactory.validate(ImportResult_1.feature()))
+
+model.begin()
+aSel = ImportResult_1.feature().selectionList("objects").value(0)
+aSel.selectSubShape("SOLID", "Part_3/Extrusion_1_1")
+model.end()
+assert(not aFactory.validate(ImportResult_1.feature()))
+
+# back to correct value
+model.begin()
+aSel = ImportResult_1.feature().selectionList("objects").value(0)
+aSel.selectSubShape("SOLID", "Part_1/Extrusion_1_1")
+model.end()
+assert(aFactory.validate(ImportResult_1.feature()))
+
+# TODO: implement for GEOMOETRICAL also
+assert(model.checkPythonDump(model.ModelHighAPI.CHECK_NAMING))
index 5fa0b85fb9f76f7912244d82c701a0fbb5afbcdb..39971790686a1080b78419d4bc15eb11d16be888 100644 (file)
@@ -45,9 +45,9 @@ SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_1.
 SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_4.result(), SketchLine_1.result())
 SketchConstraintEqual_4 = Sketch_1.setEqual(SketchLine_5.result(), SketchLine_1.result())
 SketchConstraintEqual_5 = Sketch_1.setEqual(SketchLine_6.result(), SketchLine_1.result())
-SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_2.result(), 120)
+SketchConstraintAngle_1 = Sketch_1.setAngleBackward(SketchLine_1.result(), SketchLine_2.result(), 120)
 SketchConstraintAngle_2 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), 120)
-SketchConstraintAngle_3 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_4.result(), 120)
+SketchConstraintAngle_3 = Sketch_1.setAngleBackward(SketchLine_3.result(), SketchLine_4.result(), 120)
 SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_2.result())
 SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_5.result(), 60)
 SketchProjection_1 = Sketch_1.addProjection(model.selection("VERTEX", "PartSet/Origin"), False)
diff --git a/src/FeaturesPlugin/copy_widget.xml b/src/FeaturesPlugin/copy_widget.xml
new file mode 100644 (file)
index 0000000..6f79ca1
--- /dev/null
@@ -0,0 +1,16 @@
+<source>
+  <multi_selector id="objects"
+                  label="Sources:"
+                  tooltip="Select copied objects"
+                  shape_types="vertices edges wires faces shells compsolids objects"
+                  use_choice="false"
+                  concealment="false">
+  </multi_selector>
+  <integervalue id="number"
+                label="Nb copies"
+                step="1"
+                default="1"
+                min="1"
+                tooltip="Number of copies">
+  </integervalue>
+</source>
diff --git a/src/FeaturesPlugin/defeaturing_widget.xml b/src/FeaturesPlugin/defeaturing_widget.xml
new file mode 100644 (file)
index 0000000..9a0303e
--- /dev/null
@@ -0,0 +1,12 @@
+<source>
+  <multi_selector id="main_objects"
+                  label="Faces to remove"
+                  icon=""
+                  tooltip="Select faces"
+                  shape_types="faces"
+                  use_choice="false"
+                  concealment="true">
+    <validator id="PartSet_DifferentObjects"/>
+    <validator id="FeaturesPlugin_ValidatorDefeaturingSelection"/>
+  </multi_selector>
+</source>
index f701574c7a0d137ee74088fce0e64a379b6e8a8f..91db9f48fe1e3e253a596ba62ba34020f4e0b6c5 100644 (file)
@@ -13,13 +13,16 @@ Features plug-in provides a set of common topological operations. It implements
    angularCopyFeature.rst
    chamferFeature.rst
    commonFeature.rst
+   copyFeature.rst
    cutFeature.rst
+   defeaturingFeature.rst
    extrusionCutFeature.rst
    extrusionFeature.rst
    extrusionFuseFeature.rst
    filletFeature.rst
    fuseFeature.rst
    fuseFeatureFaces.rst
+   importResult.rst
    intersectionFeature.rst
    linearCopyFeature.rst
    measurementFeature.rst
diff --git a/src/FeaturesPlugin/doc/TUI_copyFeature.rst b/src/FeaturesPlugin/doc/TUI_copyFeature.rst
new file mode 100644 (file)
index 0000000..4d7bafb
--- /dev/null
@@ -0,0 +1,12 @@
+
+  .. _tui_create_copy:
+
+Create Copy
+============
+
+.. literalinclude:: examples/copy.py
+    :linenos:
+    :language: python
+
+:download:`Download this script <examples/copy.py>` 
+   
diff --git a/src/FeaturesPlugin/doc/TUI_defeaturingFeature.rst b/src/FeaturesPlugin/doc/TUI_defeaturingFeature.rst
new file mode 100644 (file)
index 0000000..aafc733
--- /dev/null
@@ -0,0 +1,12 @@
+
+  .. _tui_defeaturing:
+
+Perform Defeaturing
+===================
+
+.. literalinclude:: examples/defeaturing.py 
+    :linenos:
+    :language: python
+
+:download:`Download this script <examples/defeaturing.py>` 
+   
diff --git a/src/FeaturesPlugin/doc/TUI_importResultFeature.rst b/src/FeaturesPlugin/doc/TUI_importResultFeature.rst
new file mode 100644 (file)
index 0000000..5d27dbe
--- /dev/null
@@ -0,0 +1,12 @@
+
+  .. _tui_create_import_result:
+
+Create Import Result
+============
+
+.. literalinclude:: examples/import_result.py
+    :linenos:
+    :language: python
+
+:download:`Download this script <examples/import_result.py>` 
+   
diff --git a/src/FeaturesPlugin/doc/copyFeature.rst b/src/FeaturesPlugin/doc/copyFeature.rst
new file mode 100644 (file)
index 0000000..9a8be49
--- /dev/null
@@ -0,0 +1,53 @@
+.. |copy_btn.icon|    image:: images/copy_btn.png
+
+Copy
+=====
+
+Copy feature makes duplicates of the selected features, results, sub-results and sub-shapes. For the whole feature selected
+all results of this feature are copied. The referenced arguments of this feature are not concealed. The history behavior of
+copy is specific: *Move to the End* of groups will move to all copy-results. For an example, if a face of a box was selected
+for a group and a copy of this box was done, the *Move to the End* of this group will cause two faces appeared in this Group:
+the original one and the copy.
+
+
+To create a Copy in the active part:
+
+#. select in the Main Menu *Features - > Copy* item  or
+#. click |copy_btn.icon| **Copy** button in the toolbar
+
+The following property panel will be opened:
+
+.. image:: images/Copy.png
+  :align: center
+
+.. centered::
+   **Copy operation**
+
+Here it is necessary to select some objects. Only results and sub-results and their sub-shapes located in the folder **Results** can be selected and copied.
+Also it is possible to increase the number of resulting copies.
+
+**Apply** button creates a copy.
+  
+**Cancel** button cancels the operation.
+
+**TUI Command**:
+
+.. py:function:: model.addCopy(Part_doc, objects, number_of_copies)
+
+    :param part: The current part object.
+    :param objects: A list of objects.
+    :param number_of_copies: A number of resulting copies.
+    :return: Result feature Copy.
+
+Result
+""""""
+
+The Result of the operation will be one or several copies of the selected shapes located in the same place:
+
+.. image:: images/CreatedCopy.png
+          :align: center
+
+.. centered::
+   **Copy created**
+
+**See Also** a sample TUI Script of :ref:`tui_create_copy` operation.
diff --git a/src/FeaturesPlugin/doc/defeaturingFeature.rst b/src/FeaturesPlugin/doc/defeaturingFeature.rst
new file mode 100644 (file)
index 0000000..33ebe85
--- /dev/null
@@ -0,0 +1,49 @@
+.. |defeaturing.icon|    image:: images/defeaturing.png
+
+Defeaturing
+===========
+
+**Defeaturing** operation is intended for removal of the unwanted parts or features from the model. These parts can be holes, protrusions, gaps, chamfers, fillets, etc. 
+
+To create a Defeaturing in the active part:
+
+#. select in the Main Menu *Feature -> Defeaturing* item  or
+#. click |defeaturing.icon| **Defeaturing** button in the toolbar.
+
+After that select one or more faces of solids to suppress them. The Defeaturing works with faces from comsolids and compounds of solids as well.
+
+.. image:: images/defeaturing_property_panel.png
+  :align: center
+
+.. centered::
+  Defeaturing property panel
+
+Input field:
+
+- **Faces to remove** panel contains the list of faces which should be suppressed.
+
+**TUI Command**:
+
+.. py:function:: model.addDefeaturing(Part_doc, [faces])
+
+    :param part: The current part object.
+    :param list: A list of faces in format *model.selection("FACE", shape)*.
+    :return: Created object.
+
+Result
+""""""
+
+Result of **Defeaturing** is shown below.
+
+.. image:: images/defeaturing_result.png
+   :align: center
+
+.. centered::
+   Defeaturing operation
+
+**See Also** a sample TUI Script of :ref:`tui_defeaturing` operation.
+
+References
+""""""""""
+
+For more information, please, visit `OpenCASCADE Documentation <https://dev.opencascade.org/doc/overview/html/occt_user_guides__modeling_algos.html#occt_modalg_defeaturing>`_.
diff --git a/src/FeaturesPlugin/doc/examples/copy.py b/src/FeaturesPlugin/doc/examples/copy.py
new file mode 100644 (file)
index 0000000..1e81c0e
--- /dev/null
@@ -0,0 +1,18 @@
+from salome.shaper import model
+
+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"))
+SketchCircle_1 = Sketch_1.addCircle(7, 11, 2)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2r")], model.selection(), 10, 0)
+Extrusion_1.result().setTransparency(0.75)
+Copy_1 = model.addCopy(Part_1_doc, [model.selection("FACE", "Extrusion_1_1/From_Face"), model.selection("FACE", "Extrusion_1_1/To_Face")], 2)
+Copy_1.result().setColor(0, 170, 0)
+Copy_1.results()[1].setColor(0, 170, 0)
+Copy_1.results()[2].setColor(0, 170, 0)
+Copy_1.results()[3].setColor(0, 170, 0)
+
+model.end()
diff --git a/src/FeaturesPlugin/doc/examples/defeaturing.py b/src/FeaturesPlugin/doc/examples/defeaturing.py
new file mode 100644 (file)
index 0000000..0dc1f52
--- /dev/null
@@ -0,0 +1,12 @@
+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)
+Fillet_1_objects = [model.selection("EDGE", "[Box_1_1/Left][Box_1_1/Top]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Left]"), model.selection("EDGE", "[Box_1_1/Front][Box_1_1/Top]")]
+Fillet_1 = model.addFillet(Part_1_doc, Fillet_1_objects, 2)
+Defeaturing_1 = model.addDefeaturing(Part_1_doc, [model.selection("FACE", "Fillet_1_1/GF:Fillet&Fillet_1_1/FilletSelected_3"), model.selection("FACE", "(Fillet_1_1/GF:Fillet&Fillet_1_1/FilletSelected_1)(Fillet_1_1/GF:Fillet&Fillet_1_1/FilletSelected_2)(Fillet_1_1/GF:Fillet&Fillet_1_1/FilletSelected_3)")])
+
+model.end()
diff --git a/src/FeaturesPlugin/doc/examples/import_result.py b/src/FeaturesPlugin/doc/examples/import_result.py
new file mode 100644 (file)
index 0000000..b0293b4
--- /dev/null
@@ -0,0 +1,15 @@
+from salome.shaper import model
+
+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"))
+SketchCircle_1 = Sketch_1.addCircle(21, 16, 6)
+model.do()
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchCircle_1_2r")], model.selection(), 10, 0)
+model.do()
+Part_2 = model.addPart(partSet)
+Part_2_doc = Part_2.document()
+ImportResult_1 = model.addImportResult(Part_2_doc, [model.selection("SOLID", "Part_1/Extrusion_1_1")])
+model.end()
diff --git a/src/FeaturesPlugin/doc/images/Copy.png b/src/FeaturesPlugin/doc/images/Copy.png
new file mode 100644 (file)
index 0000000..5304873
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/Copy.png differ
diff --git a/src/FeaturesPlugin/doc/images/CreatedCopy.png b/src/FeaturesPlugin/doc/images/CreatedCopy.png
new file mode 100644 (file)
index 0000000..f25513a
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/CreatedCopy.png differ
diff --git a/src/FeaturesPlugin/doc/images/CreatedImportResult.png b/src/FeaturesPlugin/doc/images/CreatedImportResult.png
new file mode 100644 (file)
index 0000000..3c35440
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/CreatedImportResult.png differ
diff --git a/src/FeaturesPlugin/doc/images/ImportResult.png b/src/FeaturesPlugin/doc/images/ImportResult.png
new file mode 100644 (file)
index 0000000..3ade302
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/ImportResult.png differ
diff --git a/src/FeaturesPlugin/doc/images/copy_btn.png b/src/FeaturesPlugin/doc/images/copy_btn.png
new file mode 100644 (file)
index 0000000..7e11163
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/copy_btn.png differ
diff --git a/src/FeaturesPlugin/doc/images/defeaturing.png b/src/FeaturesPlugin/doc/images/defeaturing.png
new file mode 100644 (file)
index 0000000..9b5ecc8
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/defeaturing.png differ
diff --git a/src/FeaturesPlugin/doc/images/defeaturing_property_panel.png b/src/FeaturesPlugin/doc/images/defeaturing_property_panel.png
new file mode 100644 (file)
index 0000000..d62e6ae
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/defeaturing_property_panel.png differ
diff --git a/src/FeaturesPlugin/doc/images/defeaturing_result.png b/src/FeaturesPlugin/doc/images/defeaturing_result.png
new file mode 100644 (file)
index 0000000..25b6fcd
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/defeaturing_result.png differ
diff --git a/src/FeaturesPlugin/doc/images/import_result_btn.png b/src/FeaturesPlugin/doc/images/import_result_btn.png
new file mode 100644 (file)
index 0000000..2bdd0be
Binary files /dev/null and b/src/FeaturesPlugin/doc/images/import_result_btn.png differ
diff --git a/src/FeaturesPlugin/doc/importResultFeature.rst b/src/FeaturesPlugin/doc/importResultFeature.rst
new file mode 100644 (file)
index 0000000..28741a3
--- /dev/null
@@ -0,0 +1,54 @@
+.. |import_result_btn.icon|    image:: images/import_result_btn.png
+
+Import Result
+=====
+
+The Import Result feature allows the user to import one or several results from another Part. If the result of the source-part is
+changed, the part and part result that contains the copy-results will be updated. The feature keeps the
+copy-shape, so, even the document was opened and the source-part was not activated (loaded), the part with copy-feature works well
+with this result-shape.
+
+It may be necessary for the user to load the other parts before using this feature otherwise the content of the **Results** folders will be empty.
+
+To create a Copy in the active part:
+
+#. select in the Main Menu *Features - > Import Result* item  or
+#. click |import_result_btn.icon| **Import Result** button in the toolbar
+
+
+The following property panel will be opened:
+
+.. image:: images/ImportResult.png
+  :align: center
+
+.. centered::
+   **Import result operation**
+
+Here it is necessary to select one or several objects. It is only possible to import results from Parts placed before the
+current Part where the import is done. Only results from the **Results** folder of previously created parts may be selected.
+
+
+**Apply** button creates a copy.
+  
+**Cancel** button cancels the operation.
+
+**TUI Command**:
+
+.. py:function:: model.addImportResult(Part_doc, results)
+
+    :param part: The current part object.
+    :param results: A list of results from another part.
+    :return: Result feature Import Result.
+
+Result
+""""""
+
+The Result of the operation will be copy of one or several results selected in another part located in the same place:
+
+.. image:: images/CreatedImportResult.png
+          :align: center
+
+.. centered::
+   **Import result created**
+
+**See Also** a sample TUI Script of :ref:`tui_create_import_result` operation.
index 3fbaf42c1bad584a0ad9441e851a3fce12307c39..1220c84d0f4b1117c0ff4b37cd0fb05e656d7e16 100644 (file)
@@ -29,7 +29,7 @@ Here it is necessary to select some objects. Only faces with shared edges or sol
 .. py:function:: model.addUnion(Part_doc, objects)
 
     :param part: The current part object.
-    :param list: A list of objects.
+    :param objects: A list of objects.
     :return: Result object.
 
 Result
diff --git a/src/FeaturesPlugin/icons/copy.png b/src/FeaturesPlugin/icons/copy.png
new file mode 100644 (file)
index 0000000..7e11163
Binary files /dev/null and b/src/FeaturesPlugin/icons/copy.png differ
diff --git a/src/FeaturesPlugin/icons/defeaturing.png b/src/FeaturesPlugin/icons/defeaturing.png
new file mode 100644 (file)
index 0000000..9b5ecc8
Binary files /dev/null and b/src/FeaturesPlugin/icons/defeaturing.png differ
diff --git a/src/FeaturesPlugin/icons/import_result.png b/src/FeaturesPlugin/icons/import_result.png
new file mode 100644 (file)
index 0000000..2bdd0be
Binary files /dev/null and b/src/FeaturesPlugin/icons/import_result.png differ
diff --git a/src/FeaturesPlugin/import_result_widget.xml b/src/FeaturesPlugin/import_result_widget.xml
new file mode 100644 (file)
index 0000000..94d1f5e
--- /dev/null
@@ -0,0 +1,11 @@
+<source>
+  <multi_selector id="objects"
+                  label="Sources:"
+                  tooltip="Select copied results"
+                  shape_types="objects"
+                  use_choice="false"
+                  concealment="false"
+                  allow_parts_content="true">
+    <validator id="FeaturesPlugin_ValidatorImportResults"/>
+  </multi_selector>
+</source>
index 3197488a7e3870b1b58ad4d5ed13060f249a736d..cba1b4292fbb3984de3503a22d9e2d1a67284638 100644 (file)
         helpfile="recoverFeature.html">
         <source path="recover_widget.xml"/>
       </feature>
+      <feature id="Copy"
+        title="Copy"
+        tooltip="Copies results or sub-results"
+        icon="icons/Features/copy.png"
+        helpfile="copyFeature.html">
+        <source path="copy_widget.xml"/>
+      </feature>
+      <feature id="ImportResult"
+        title="Import Result"
+        tooltip="Copies results from other parts"
+        icon="icons/Features/import_result.png"
+        helpfile="importResultFeature.html">
+        <source path="import_result_widget.xml"/>
+      </feature>
       <feature id="RemoveResults" title="Remove results" tooltip="Internal feature for results removal" internal="1">
         <multi_selector id="results" concealment="true"/>
       </feature>
     </group>
-    <group id="Fillet">
+    <group id="Features">
       <feature id="Fillet" title="Fillet" tooltip="Perform fillet on face or edge"
                icon="icons/Features/fillet.png" auto_preview="true" helpfile="filletFeature.html">
         <source path="fillet_widget.xml"/>
                icon="icons/Features/fusion_faces.png" auto_preview="true" helpfile="FeaturesPlugin/fusionFacesFeature.html">
         <source path="fusion_faces_widget.xml"/>
       </feature>
+      <feature id="Defeaturing" title="Defeaturing" tooltip="Perform removing faces from solid"
+               icon="icons/Features/defeaturing.png" auto_preview="true" helpfile="defeaturingFeature.html">
+        <source path="defeaturing_widget.xml"/>
+      </feature>
     </group>
   </workbench>
   <workbench id="Part">
         <source path="multirotation_widget.xml"/>
       </feature>
     </group>
+  </workbench>
+  <workbench id="Inspection">
     <group id="Measurement">
       <feature id="Measurement" title="Measurement" tooltip="Calculate properties of objects"
                icon="icons/Features/measurement.png" helpfile="measurementFeature.html" abort_confirmation="false">
index cade6fc2f16ff15464185570981dc706b74fd3b4..267c703a9c5f5160db59c157b4f30ccf0fe22a99 100644 (file)
@@ -25,6 +25,8 @@ INCLUDE(UnitTest)
 
 SET(PROJECT_HEADERS
     GeomAPI.h
+    GeomAPI_BSpline.h
+    GeomAPI_BSpline2d.h
     GeomAPI_Circ.h
     GeomAPI_Circ2d.h
     GeomAPI_Interface.h
@@ -71,6 +73,8 @@ SET(PROJECT_HEADERS
 )
 
 SET(PROJECT_SOURCES
+    GeomAPI_BSpline.cpp
+    GeomAPI_BSpline2d.cpp
     GeomAPI_Circ.cpp
     GeomAPI_Circ2d.cpp
     GeomAPI_Interface.cpp
index 4c4110155cfe305b7d424d90bc1b9dfd97648cb4..4b264be7067885214662a605336435f05a92b4f1 100644 (file)
@@ -42,6 +42,8 @@
 %shared_ptr(GeomAPI_Ax2)
 %shared_ptr(GeomAPI_Ax3)
 %shared_ptr(GeomAPI_Box)
+%shared_ptr(GeomAPI_BSpline)
+%shared_ptr(GeomAPI_BSpline2d)
 %shared_ptr(GeomAPI_Circ)
 %shared_ptr(GeomAPI_Circ2d)
 %shared_ptr(GeomAPI_Cone)
   }
 }
 
-%typemap(in) double & (double temp) {
-  if (PyLong_Check($input)) {
-    temp = PyLong_AsLong($input);
-    $1 = &temp;
-  }
+%typemap(in, numinputs=0) double & (double temp) {
+  $1 = &temp;
 }
 
 %typemap(argout) double & {
-  $result = PyFloat_FromDouble(*$1);
+  $result = SWIG_Python_AppendOutput($result, PyFloat_FromDouble(*$1));
 }
 
+// std::dynamic_pointer_cast
+template<class T1, class T2> std::shared_ptr<T1> shared_ptr_cast(std::shared_ptr<T2> theObject);
+%template(shapeToEdge) shared_ptr_cast<GeomAPI_Edge, GeomAPI_Shape>;
+
 
 // all supported interfaces
 %include "GeomAPI_Interface.h"
 %include "GeomAPI_Ax2.h"
 %include "GeomAPI_Ax3.h"
 %include "GeomAPI_Box.h"
+%include "GeomAPI_BSpline.h"
+%include "GeomAPI_BSpline2d.h"
 %include "GeomAPI_Circ.h"
 %include "GeomAPI_Circ2d.h"
 %include "GeomAPI_Cone.h"
diff --git a/src/GeomAPI/GeomAPI_BSpline.cpp b/src/GeomAPI/GeomAPI_BSpline.cpp
new file mode 100644 (file)
index 0000000..cb2aef9
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <GeomAPI_BSpline.h>
+#include <GeomAPI_Pnt.h>
+
+#include <Geom_BSplineCurve.hxx>
+
+#define MY_BSPLINE (*(implPtr<Handle_Geom_BSplineCurve>()))
+
+GeomAPI_BSpline::GeomAPI_BSpline(const GeomCurvePtr& theCurve)
+{
+  GeomCurvePtr anUntrimmedCurve = theCurve->basisCurve();
+  Handle(Geom_Curve) aCurve = anUntrimmedCurve->impl<Handle(Geom_Curve)>();
+  Handle(Geom_BSplineCurve) aBSpl = Handle(Geom_BSplineCurve)::DownCast(aCurve);
+  if (aBSpl.IsNull())
+    throw Standard_ConstructionError("GeomAPI_BSpline: Curve is not a B-spline");
+  setImpl(new Handle_Geom_BSplineCurve(aBSpl));
+}
+
+int GeomAPI_BSpline::degree() const
+{
+  return MY_BSPLINE->Degree();
+}
+
+std::list<GeomPointPtr> GeomAPI_BSpline::poles() const
+{
+  const TColgp_Array1OfPnt& aBSplPoles = MY_BSPLINE->Poles();
+
+  std::list<GeomPointPtr> aPoles;
+  for (int anIndex = aBSplPoles.Lower(); anIndex <= aBSplPoles.Upper(); ++anIndex) {
+    const gp_Pnt& aPoint = aBSplPoles.Value(anIndex);
+    aPoles.push_back(GeomPointPtr(new GeomAPI_Pnt(aPoint.X(), aPoint.Y(), aPoint.Z())));
+  }
+  return aPoles;
+}
+
+std::list<double> GeomAPI_BSpline::weights() const
+{
+  std::list<double> aWeights;
+  const TColStd_Array1OfReal* aBSplWeights = MY_BSPLINE->Weights();
+  if (aBSplWeights)
+    aWeights.assign(aBSplWeights->begin(), aBSplWeights->end());
+  return aWeights;
+}
+
+std::list<double> GeomAPI_BSpline::knots() const
+{
+  const TColStd_Array1OfReal& aBSplKnots = MY_BSPLINE->Knots();
+  return std::list<double>(aBSplKnots.begin(), aBSplKnots.end());
+}
+
+std::list<int> GeomAPI_BSpline::mults() const
+{
+  const TColStd_Array1OfInteger& aBSplMults = MY_BSPLINE->Multiplicities();
+  return std::list<int>(aBSplMults.begin(), aBSplMults.end());
+}
+
+bool GeomAPI_BSpline::isPeriodic() const
+{
+  return MY_BSPLINE->IsPeriodic();
+}
diff --git a/src/GeomAPI/GeomAPI_BSpline.h b/src/GeomAPI/GeomAPI_BSpline.h
new file mode 100644 (file)
index 0000000..95468be
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef GeomAPI_BSpline_H_
+#define GeomAPI_BSpline_H_
+
+#include <GeomAPI_Interface.h>
+#include <GeomAPI_Curve.h>
+
+#include <list>
+#include <memory>
+
+class GeomAPI_Pnt;
+
+/**\class GeomAPI_BSpline
+ * \ingroup DataModel
+ * \brief B-spline in 3D
+ */
+class GeomAPI_BSpline : public GeomAPI_Interface
+{
+public:
+  /// Creation of B-spline defined by a curve
+  GEOMAPI_EXPORT GeomAPI_BSpline(const GeomCurvePtr& theCurve);
+
+  /// Degree of B-spline curve
+  GEOMAPI_EXPORT int degree() const;
+
+  /// Poles of B-spline curve
+  GEOMAPI_EXPORT std::list<std::shared_ptr<GeomAPI_Pnt> > poles() const;
+
+  /// Weights of B-spline poles
+  GEOMAPI_EXPORT std::list<double> weights() const;
+
+  /// Knots of B-spline curve
+  GEOMAPI_EXPORT std::list<double> knots() const;
+
+  /// Multiplicities of B-spline knots
+  GEOMAPI_EXPORT std::list<int> mults() const;
+
+  /// Return \c true if the curve is periodic
+  GEOMAPI_EXPORT bool isPeriodic() const;
+};
+
+//! Pointer on the object
+typedef std::shared_ptr<GeomAPI_BSpline> GeomBSplinePtr;
+
+#endif
diff --git a/src/GeomAPI/GeomAPI_BSpline2d.cpp b/src/GeomAPI/GeomAPI_BSpline2d.cpp
new file mode 100644 (file)
index 0000000..f702a27
--- /dev/null
@@ -0,0 +1,201 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <GeomAPI_BSpline2d.h>
+#include <GeomAPI_Pnt2d.h>
+#include <GeomAPI_XY.h>
+
+#include <Geom2d_BSplineCurve.hxx>
+#include <Geom2dAPI_ProjectPointOnCurve.hxx>
+#include <GeomLib_Tool.hxx>
+#include <Precision.hxx>
+
+#define MY_BSPLINE (*(implPtr<Handle_Geom2d_BSplineCurve>()))
+
+
+static Handle_Geom2d_BSplineCurve* newBSpline2d(
+  const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+  const std::list<double>& theWeights,
+  const int theDegree,
+  const bool thePeriodic);
+
+
+static Handle_Geom2d_BSplineCurve* newBSpline2d(
+    const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+    const std::list<double>& theWeights,
+    const std::list<double>& theKnots,
+    const std::list<int>& theMults,
+    const int theDegree,
+    const bool thePeriodic)
+{
+  if (theKnots.empty() || theMults.empty())
+    return newBSpline2d(thePoles, theWeights, theDegree, thePeriodic);
+
+  int anAuxPole = 0;
+  if (thePeriodic && thePoles.front()->distance(thePoles.back()) < Precision::Confusion())
+    anAuxPole = -1;
+
+  // collect arrays of poles, weights, knots and multiplicities
+  TColgp_Array1OfPnt2d aPoles(1, (int)thePoles.size() + anAuxPole);
+  TColStd_Array1OfReal aWeights(1, (int)theWeights.size() + anAuxPole);
+  TColStd_Array1OfReal aKnots(1, (int)theKnots.size());
+  TColStd_Array1OfInteger aMults(1, (int)theMults.size());
+
+  int anIndex = 1;
+  for (std::list<GeomPnt2dPtr>::const_iterator aPIt = thePoles.begin();
+       aPIt != thePoles.end() && anIndex <= aPoles.Upper(); ++aPIt, ++anIndex)
+    aPoles.SetValue(anIndex, gp_Pnt2d((*aPIt)->x(), (*aPIt)->y()));
+  anIndex = 1;
+  for (std::list<double>::const_iterator aWIt = theWeights.begin();
+       aWIt != theWeights.end() && anIndex <= aWeights.Upper(); ++aWIt, ++anIndex)
+    aWeights.SetValue(anIndex, *aWIt);
+  anIndex = 1;
+  for (std::list<double>::const_iterator aKIt = theKnots.begin();
+       aKIt != theKnots.end(); ++aKIt, ++anIndex)
+    aKnots.SetValue(anIndex, *aKIt);
+  anIndex = 1;
+  for (std::list<int>::const_iterator aMIt = theMults.begin();
+       aMIt != theMults.end(); ++aMIt, ++anIndex)
+    aMults.SetValue(anIndex, *aMIt);
+
+  Handle(Geom2d_BSplineCurve) aCurve =
+      new Geom2d_BSplineCurve(aPoles, aWeights, aKnots, aMults, theDegree, thePeriodic);
+  return new Handle_Geom2d_BSplineCurve(aCurve);
+}
+
+Handle_Geom2d_BSplineCurve* newBSpline2d(
+    const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+    const std::list<double>& theWeights,
+    const int theDegree,
+    const bool thePeriodic)
+{
+  std::list<std::shared_ptr<GeomAPI_Pnt2d> > aPoles = thePoles;
+  std::list<double> aWeights = theWeights;
+  int aMult = theDegree + 1;
+  int aNbKnots = (int)thePoles.size() - theDegree + 1;
+  if (thePeriodic) {
+    if (aPoles.front()->distance(aPoles.back()) < Precision::Confusion()) {
+      aPoles.pop_back();
+      aWeights.pop_back();
+    }
+    aMult = 1;
+    aNbKnots = (int)aPoles.size() + 1;
+  }
+
+  if (aNbKnots < 2)
+    return new Handle_Geom2d_BSplineCurve();
+
+  static const double aStartParam = 0.0;
+  static const double aEndParam = 1.0;
+  double aStep = aEndParam / (aNbKnots - 1);
+  int anIndex = 1;
+  std::list<double> aKnots;
+  for (double aKnot = aStartParam; anIndex < aNbKnots; ++anIndex, aKnot += aStep)
+    aKnots.push_back(aKnot);
+  aKnots.push_back(aEndParam);
+
+  std::list<int> aMults(aNbKnots - 2, 1);
+  aMults.push_front(aMult);
+  aMults.push_back(aMult);
+
+  return newBSpline2d(aPoles, aWeights, aKnots, aMults, theDegree, thePeriodic);
+}
+
+static Handle_Geom2d_BSplineCurve* newBSpline2d(
+    const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+    const std::list<double>& theWeights,
+    const bool thePeriodic)
+{
+  int aDegree = 3;
+  if ((int)thePoles.size() <= aDegree)
+    aDegree = (int)thePoles.size() - 1;
+  if (aDegree <= 0)
+    return new Handle_Geom2d_BSplineCurve();
+  return newBSpline2d(thePoles, theWeights, aDegree, thePeriodic);
+}
+
+
+GeomAPI_BSpline2d::GeomAPI_BSpline2d(const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                                     const std::list<double>& theWeights,
+                                     const bool thePeriodic)
+  : GeomAPI_Interface(newBSpline2d(thePoles, theWeights, thePeriodic))
+{
+  if (isNull())
+    throw Standard_ConstructionError("GeomAPI_BSpline2d: Impossible to create B-spline curve");
+}
+
+GeomAPI_BSpline2d::GeomAPI_BSpline2d(const int theDegree,
+                                     const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                                     const std::list<double>& theWeights,
+                                     const std::list<double>& theKnots,
+                                     const std::list<int>& theMults,
+                                     const bool thePeriodic)
+  : GeomAPI_Interface(newBSpline2d(thePoles, theWeights, theKnots, theMults,
+                                   theDegree, thePeriodic))
+{
+  if (isNull())
+    throw Standard_ConstructionError("GeomAPI_BSpline2d: Impossible to create B-spline curve");
+}
+
+bool GeomAPI_BSpline2d::isNull() const
+{
+  return MY_BSPLINE.IsNull();
+}
+
+int GeomAPI_BSpline2d::degree() const
+{
+  return MY_BSPLINE->Degree();
+}
+
+std::list<double> GeomAPI_BSpline2d::knots() const
+{
+  const TColStd_Array1OfReal& aBSplKnots = MY_BSPLINE->Knots();
+  return std::list<double>(aBSplKnots.begin(), aBSplKnots.end());
+}
+
+std::list<int> GeomAPI_BSpline2d::mults() const
+{
+  const TColStd_Array1OfInteger& aBSplMults = MY_BSPLINE->Multiplicities();
+  return std::list<int>(aBSplMults.begin(), aBSplMults.end());
+}
+
+const bool GeomAPI_BSpline2d::parameter(const std::shared_ptr<GeomAPI_Pnt2d> thePoint,
+                                        const double theTolerance,
+                                        double& theParameter) const
+{
+  return GeomLib_Tool::Parameter(MY_BSPLINE, thePoint->impl<gp_Pnt2d>(),
+                                 theTolerance, theParameter) == Standard_True;
+}
+
+void GeomAPI_BSpline2d::D0(const double theU, std::shared_ptr<GeomAPI_Pnt2d>& thePoint)
+{
+  gp_Pnt2d aPnt;
+  MY_BSPLINE->D0(theU, aPnt);
+  thePoint.reset(new GeomAPI_Pnt2d(aPnt.X(), aPnt.Y()));
+}
+
+void GeomAPI_BSpline2d::D1(const double theU, std::shared_ptr<GeomAPI_Pnt2d>& thePoint,
+                                              std::shared_ptr<GeomAPI_XY>& theDerivative)
+{
+  gp_Pnt2d aPnt;
+  gp_Vec2d aVec;
+  MY_BSPLINE->D1(theU, aPnt, aVec);
+  thePoint.reset(new GeomAPI_Pnt2d(aPnt.X(), aPnt.Y()));
+  theDerivative.reset(new GeomAPI_XY(aVec.X(), aVec.Y()));
+}
diff --git a/src/GeomAPI/GeomAPI_BSpline2d.h b/src/GeomAPI/GeomAPI_BSpline2d.h
new file mode 100644 (file)
index 0000000..260577b
--- /dev/null
@@ -0,0 +1,83 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef GeomAPI_BSpline2d_H_
+#define GeomAPI_BSpline2d_H_
+
+#include <GeomAPI_Interface.h>
+
+#include <list>
+#include <memory>
+
+class GeomAPI_Pnt2d;
+class GeomAPI_XY;
+
+/** \class GeomAPI_BSpline2d
+ *  \ingroup DataModel
+ *  \brief B-spline curve in 2D
+ */
+class GeomAPI_BSpline2d : public GeomAPI_Interface
+{
+public:
+  /// Creation of B-spline curve defined by list of poles and weights
+  GEOMAPI_EXPORT GeomAPI_BSpline2d(const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                                   const std::list<double>& theWeights,
+                                   const bool thePeriodic = false);
+
+  /// Creation of B-spline curve defined by list of poles and weights
+  GEOMAPI_EXPORT GeomAPI_BSpline2d(const int theDegree,
+                                   const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                                   const std::list<double>& theWeights,
+                                   const std::list<double>& theKnots = std::list<double>(),
+                                   const std::list<int>& theMults = std::list<int>(),
+                                   const bool thePeriodic = false);
+
+  /// Returns true if curve is not initialized
+  GEOMAPI_EXPORT bool isNull() const;
+
+  /// Returns degree of the curve
+  GEOMAPI_EXPORT int degree() const;
+
+  /// Knots of the curve
+  GEOMAPI_EXPORT std::list<double> knots() const;
+
+  /// Multiplicities of the knots
+  GEOMAPI_EXPORT std::list<int> mults() const;
+
+  /// \brief Computes the parameter of a given point on a circle. The point must be
+  ///        located either on the circle itself or relatively to the latter
+  ///        at a distance less than the tolerance value. Return FALSE if the point
+  ///        is beyond the tolerance limit or if computation fails.
+  ///        Max Tolerance value is currently limited to 1.e-4
+  /// \param[in] thePoint point of origin.
+  /// \param[in] theTolerance tolerance of computation.
+  /// \param[out] theParameter resulting parameter.
+  GEOMAPI_EXPORT const bool parameter(const std::shared_ptr<GeomAPI_Pnt2d> thePoint,
+                                      const double theTolerance,
+                                      double& theParameter) const;
+
+  /// \brief Calculate point on B-spline curve accrding to the given parameter
+  GEOMAPI_EXPORT void D0(const double theU, std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
+
+  /// \brief Calculate point and first derivative for B-spline curve accrding to the given parameter
+  GEOMAPI_EXPORT void D1(const double theU, std::shared_ptr<GeomAPI_Pnt2d>& thePoint,
+                                            std::shared_ptr<GeomAPI_XY>& theDerivative);
+};
+
+#endif
index c4504272b3ccbea972eb5606d69f5288aab6da0a..03868875ba53de38688ae1abddfed3e399a942e7 100644 (file)
@@ -36,6 +36,7 @@
 #include <BRep_Tool.hxx>
 #include <ElCLib.hxx>
 #include <GCPnts_UniformAbscissa.hxx>
+#include <Geom_BSplineCurve.hxx>
 #include <Geom_Curve.hxx>
 #include <Geom_Line.hxx>
 #include <Geom_Circle.hxx>
@@ -179,6 +180,18 @@ bool GeomAPI_Edge::isEllipse() const
   return false;
 }
 
+bool GeomAPI_Edge::isBSpline() const
+{
+  const TopoDS_Shape& aShape = const_cast<GeomAPI_Edge*>(this)->impl<TopoDS_Shape>();
+  double aFirst, aLast;
+  Handle(Geom_Curve) aCurve = BRep_Tool::Curve((const TopoDS_Edge&)aShape, aFirst, aLast);
+  if (aCurve.IsNull()) // degenerative edge
+    return false;
+  while (aCurve->IsKind(STANDARD_TYPE(Geom_TrimmedCurve)))
+    aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
+  return aCurve->IsKind(STANDARD_TYPE(Geom_BSplineCurve));
+}
+
 std::shared_ptr<GeomAPI_Pnt> GeomAPI_Edge::firstPoint()
 {
   const TopoDS_Shape& aShape = const_cast<GeomAPI_Edge*>(this)->impl<TopoDS_Shape>();
@@ -294,13 +307,26 @@ bool GeomAPI_Edge::isEqual(const std::shared_ptr<GeomAPI_Shape> theEdge) const
   return true;
 }
 
-// LCOV_EXCL_START
+void GeomAPI_Edge::setRange(const double& theFirst, const double& theLast)
+{
+  TopoDS_Edge anEdge = impl<TopoDS_Edge>();
+  double aFirst, aLast;
+  Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
+  double aTolerance = BRep_Tool::Tolerance(anEdge);
+  if (aCurve->DynamicType() == STANDARD_TYPE(Geom_TrimmedCurve)) {
+    aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
+    BRep_Builder().UpdateEdge(anEdge, aCurve, aTolerance);
+  }
+  BRep_Builder().Range(anEdge, theFirst, theLast);
+}
+
 void GeomAPI_Edge::getRange(double& theFirst, double& theLast) const
 {
   const TopoDS_Shape& aShape = const_cast<GeomAPI_Edge*>(this)->impl<TopoDS_Shape>();
   Handle(Geom_Curve) aCurve = BRep_Tool::Curve((const TopoDS_Edge&)aShape, theFirst, theLast);
 }
 
+// LCOV_EXCL_START
 bool GeomAPI_Edge::isInPlane(std::shared_ptr<GeomAPI_Pln> thePlane) const
 {
   double aFirst, aLast;
index fdf82ed6fb57954343676c3969fa3805791f44a1..3e926d8d04e5d109cb512957bc4eeed805425c10 100644 (file)
@@ -70,6 +70,10 @@ public:
   GEOMAPI_EXPORT
   bool isEllipse() const;
 
+  /// Verifies that the edge is based on a B-spline curve
+  GEOMAPI_EXPORT
+  bool isBSpline() const;
+
   /// Returns the first vertex coordinates of the edge
   GEOMAPI_EXPORT
   std::shared_ptr<GeomAPI_Pnt> firstPoint();
@@ -94,6 +98,10 @@ public:
   GEOMAPI_EXPORT
   bool isEqual(const std::shared_ptr<GeomAPI_Shape> theEdge) const;
 
+  /// Change parametric range of the curve
+  GEOMAPI_EXPORT
+  void setRange(const double& theFirst, const double& theLast);
+
   /// Returns range of parameter on the curve
   GEOMAPI_EXPORT
   void getRange(double& theFirst, double& theLast) const;
index 60ca800a3d03c75c06a90a647fa4cfb8a0c9df71..78a10ac3c5ffb0bb6940a29828e1a07dca6c034d 100644 (file)
@@ -758,3 +758,21 @@ bool GeomAPI_Shape::ComparatorWithOri::operator()(
   }
   return isLess;
 }
+
+int GeomAPI_Shape::Hash::operator()(const std::shared_ptr<GeomAPI_Shape>& theShape) const
+{
+  const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
+  return aShape.HashCode(IntegerLast());
+}
+
+bool GeomAPI_Shape::Equal::operator()(const std::shared_ptr<GeomAPI_Shape>& theShape1,
+                                      const std::shared_ptr<GeomAPI_Shape>& theShape2) const
+{
+  const TopoDS_Shape& aShape1 = theShape1->impl<TopoDS_Shape>();
+  const TopoDS_Shape& aShape2 = theShape2->impl<TopoDS_Shape>();
+
+  Standard_Integer aHash1 = aShape1.Location().HashCode(IntegerLast());
+  Standard_Integer aHash2 = aShape2.Location().HashCode(IntegerLast());
+
+  return aShape1.TShape() == aShape2.TShape() && aHash1 == aHash2;
+}
index 34887d85f3aaa3b85b14d641b4a3b8e844ba9e61..61fd4f5bc36372462ed275eaed21fa02e3edb47d 100644 (file)
@@ -249,6 +249,25 @@ public:
       bool operator ()(const std::shared_ptr<GeomAPI_Shape>& theShape1,
                        const std::shared_ptr<GeomAPI_Shape>& theShape2) const;
   };
+
+  /// \brief Hash code for the shape
+  class Hash
+  {
+  public:
+    /// Return Hash value according to the address of the shape
+    GEOMAPI_EXPORT
+    int operator ()(const std::shared_ptr<GeomAPI_Shape>& theShape) const;
+  };
+
+  /// \brief Compare addresses of shapes
+  class Equal
+  {
+  public:
+    /// Return \c true if the address of the shapes are equal
+    GEOMAPI_EXPORT
+    bool operator ()(const std::shared_ptr<GeomAPI_Shape>& theShape1,
+                     const std::shared_ptr<GeomAPI_Shape>& theShape2) const;
+  };
 };
 
 //! Pointer on list of shapes
index 8c5472e93649d10d94fffd67657c34a13ba88cc3..fca55e7932e8d8e7ae3275090816366d4d8397ce 100644 (file)
@@ -28,6 +28,8 @@
   #include "GeomAPI_Ax2.h"
   #include "GeomAPI_Ax3.h"
   #include "GeomAPI_Box.h"
+  #include "GeomAPI_BSpline.h"
+  #include "GeomAPI_BSpline2d.h"
   #include "GeomAPI_Circ.h"
   #include "GeomAPI_Circ2d.h"
   #include "GeomAPI_Cone.h"
   #include <memory>
   #include <string>
 
+  template<class T1, class T2>
+  std::shared_ptr<T1> shared_ptr_cast(std::shared_ptr<T2> theObject)
+  {
+    return std::dynamic_pointer_cast<T1>(theObject);
+  }
+
 #endif /* SRC_GEOMAPI_GEOMAPI_SWIG_H_ */
index 5861ac8d81332276b0175babe5bcc5af6ad7910a..391acc8f641b11991f567fbccd7aa4a64b7ecf3b 100644 (file)
@@ -84,6 +84,7 @@ SET(PROJECT_HEADERS
     GeomAlgoAPI_MapShapesAndAncestors.h
     GeomAlgoAPI_Projection.h
     GeomAlgoAPI_Chamfer.h
+    GeomAlgoAPI_Defeaturing.h
 )
 
 SET(PROJECT_SOURCES
@@ -147,6 +148,7 @@ SET(PROJECT_SOURCES
     GeomAlgoAPI_MapShapesAndAncestors.cpp
     GeomAlgoAPI_Projection.cpp
     GeomAlgoAPI_Chamfer.cpp
+    GeomAlgoAPI_Defeaturing.cpp
 )
 
 SET(PROJECT_LIBRARIES
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Defeaturing.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_Defeaturing.cpp
new file mode 100644 (file)
index 0000000..9b058e8
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright (C) 2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <GeomAlgoAPI_Defeaturing.h>
+#include <GeomAlgoAPI_DFLoader.h>
+
+#include <BRepAlgoAPI_Defeaturing.hxx>
+
+GeomAlgoAPI_Defeaturing::GeomAlgoAPI_Defeaturing(const GeomShapePtr& theBaseSolid,
+                                                 const ListOfShape&  theFacesToRemove)
+{
+  build(theBaseSolid, theFacesToRemove);
+}
+
+void GeomAlgoAPI_Defeaturing::build(const GeomShapePtr& theBaseSolid,
+                                    const ListOfShape&  theFacesToRemove)
+{
+  if (!theBaseSolid || theFacesToRemove.empty())
+    return;
+
+  BRepAlgoAPI_Defeaturing* aDefeaturing = new BRepAlgoAPI_Defeaturing;
+  aDefeaturing->SetShape(theBaseSolid->impl<TopoDS_Shape>());
+  aDefeaturing->SetRunParallel(Standard_True);
+
+  // collect faces to remove
+  TopTools_ListOfShape aFaces;
+  for (ListOfShape::const_iterator anIt = theFacesToRemove.begin();
+       anIt != theFacesToRemove.end(); ++anIt)
+    aDefeaturing->AddFaceToRemove((*anIt)->impl<TopoDS_Shape>());
+
+  setImpl(aDefeaturing);
+  setBuilderType(OCCT_BRepBuilderAPI_MakeShape);
+
+  // build and get result
+  aDefeaturing->Build();
+  if (!aDefeaturing->IsDone() || aDefeaturing->HasErrors() || aDefeaturing->HasWarnings()) {
+    std::ostringstream errors;
+    aDefeaturing->DumpErrors(errors);
+    aDefeaturing->DumpWarnings(errors);
+    myError = errors.str();
+    return;
+  }
+
+  TopoDS_Shape aResult = GeomAlgoAPI_DFLoader::refineResult(aDefeaturing->Shape());
+
+  std::shared_ptr<GeomAPI_Shape> aShape(new GeomAPI_Shape());
+  aShape->setImpl(new TopoDS_Shape(aResult));
+  setShape(aShape);
+  setDone(true);
+}
diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_Defeaturing.h b/src/GeomAlgoAPI/GeomAlgoAPI_Defeaturing.h
new file mode 100644 (file)
index 0000000..04a1bf0
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright (C) 2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef GeomAlgoAPI_Defeaturing_H_
+#define GeomAlgoAPI_Defeaturing_H_
+
+#include <GeomAlgoAPI.h>
+#include <GeomAlgoAPI_MakeShape.h>
+
+#include <GeomAPI_Shape.h>
+
+/// \class GeomAlgoAPI_Defeaturing
+/// \ingroup DataAlgo
+/// \brief Perform Defeaturing algorithm
+class GeomAlgoAPI_Defeaturing : public GeomAlgoAPI_MakeShape
+{
+public:
+  /// Run Defeaturing operation on the solid for the given list of faces.
+  /// \param theBaseSolid     a changing solid
+  /// \param theFacesToRemove list of faces to be removed
+  GEOMALGOAPI_EXPORT GeomAlgoAPI_Defeaturing(const GeomShapePtr& theBaseSolid,
+                                             const ListOfShape&  theFacesToRemove);
+
+private:
+  /// Perform Defeaturing operation.
+  void build(const GeomShapePtr& theBaseSolid,
+             const ListOfShape&  theFacesToRemove);
+};
+
+#endif
index 9bfc1a50a20364ac3d8c4af28ca2c23fb34d9bd8..3badacb17afb6aa9eab7ed363672246731558225 100644 (file)
 #include <GeomAlgoAPI_EdgeBuilder.h>
 
 #include <GeomAPI_Ax2.h>
+#include <GeomAPI_Ax3.h>
+#include <GeomAPI_BSpline2d.h>
 #include <GeomAPI_Ellipse.h>
+#include <GeomAPI_Pnt2d.h>
 
 #include <gp_Pln.hxx>
 #include <BRepBuilderAPI_MakeEdge.hxx>
 #include <TopoDS_Face.hxx>
 #include <TopoDS.hxx>
 #include <BRep_Tool.hxx>
+#include <Geom2d_BSplineCurve.hxx>
 #include <Geom_Plane.hxx>
 #include <Geom_CylindricalSurface.hxx>
 #include <Geom_RectangularTrimmedSurface.hxx>
+#include <GeomLib.hxx>
 
 #include <gp_Ax2.hxx>
 #include <gp_Circ.hxx>
@@ -266,3 +271,31 @@ std::shared_ptr<GeomAPI_Edge> GeomAlgoAPI_EdgeBuilder::ellipticArc(
   aRes->setImpl(new TopoDS_Shape(anEdge));
   return aRes;
 }
+
+GeomEdgePtr GeomAlgoAPI_EdgeBuilder::bsplineOnPlane(
+    const std::shared_ptr<GeomAPI_Ax3>& thePlane,
+    const std::list<GeomPnt2dPtr>& thePoles,
+    const std::list<double>& theWeights,
+    const std::list<double>& theKnots,
+    const std::list<int>& theMults,
+    const int theDegree,
+    const bool thePeriodic)
+{
+  std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve(
+      new GeomAPI_BSpline2d(theDegree, thePoles, theWeights, theKnots, theMults, thePeriodic));
+  return bsplineOnPlane(thePlane, aBSplineCurve);
+}
+
+GeomEdgePtr GeomAlgoAPI_EdgeBuilder::bsplineOnPlane(
+    const std::shared_ptr<GeomAPI_Ax3>& thePlane,
+    const std::shared_ptr<GeomAPI_BSpline2d>& theCurve)
+{
+  Handle(Geom_Curve) aCurve3D = GeomLib::To3d(thePlane->impl<gp_Ax3>().Ax2(),
+                                              theCurve->impl<Handle_Geom2d_BSplineCurve>());
+
+  BRepBuilderAPI_MakeEdge anEdgeBuilder(aCurve3D);
+  GeomEdgePtr aRes(new GeomAPI_Edge);
+  TopoDS_Edge anEdge = anEdgeBuilder.Edge();
+  aRes->setImpl(new TopoDS_Shape(anEdge));
+  return aRes;
+}
index 245e97f05888c0795f2d21e37dde3f242ae10e05..3b6a8c760659b80f6d09485347d7e407a575f31c 100644 (file)
 #include <GeomAPI_Lin.h>
 #include <GeomAPI_Circ.h>
 #include <memory>
+#include <vector>
+
+class GeomAPI_Ax3;
+class GeomAPI_BSpline2d;
 
 /**\class GeomAlgoAPI_EdgeBuilder
  * \ingroup DataAlgo
@@ -89,6 +93,19 @@ class GEOMALGOAPI_EXPORT GeomAlgoAPI_EdgeBuilder
       const double                        theMinorRadius,
       const std::shared_ptr<GeomAPI_Pnt>& theStart,
       const std::shared_ptr<GeomAPI_Pnt>& theEnd);
+
+  /// Creates planar B-spline edge
+  static GeomEdgePtr bsplineOnPlane(const std::shared_ptr<GeomAPI_Ax3>& thePlane,
+                                    const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                                    const std::list<double>& theWeights,
+                                    const std::list<double>& theKnots,
+                                    const std::list<int>& theMults,
+                                    const int theDegree,
+                                    const bool thePeriodic);
+
+  /// Creates planar B-spline edge
+  static GeomEdgePtr bsplineOnPlane(const std::shared_ptr<GeomAPI_Ax3>& thePlane,
+                                    const std::shared_ptr<GeomAPI_BSpline2d>& theCurve);
 };
 
 #endif
index cfed1f1992a766cb871a625e2ffa467da73ddaed..b8d49ef679664a2c5fc209278132eed341754d6f 100644 (file)
@@ -23,7 +23,7 @@
 #include <GeomAlgoAPI_DFLoader.h>
 #include <GeomAlgoAPI_ShapeTools.h>
 
-#include <ShapeUpgrade_UnifySameDomain.hxx>
+#include <BRep_Tool.hxx>
 #include <TopExp_Explorer.hxx>
 #include <TopoDS_Shape.hxx>
 #include <Precision.hxx>
@@ -31,6 +31,7 @@
 #include <TopoDS_Edge.hxx>
 #include <Bnd_Box.hxx>
 #include <BRepBndLib.hxx>
+#include <ShapeUpgrade_UnifySameDomain.hxx>
 
 //==================================================================================================
 GeomAlgoAPI_UnifySameDomain::GeomAlgoAPI_UnifySameDomain(const ListOfShape& theShapes)
@@ -72,29 +73,18 @@ void GeomAlgoAPI_UnifySameDomain::build(const ListOfShape& theShapes)
 }
 
 // calculates maximum possible tolerance on edges of shape
-// (method from GEOM module BlockFix_UnionFaces.cxx)
 static Standard_Real defineLinearTolerance(const TopoDS_Shape& theShape)
 {
-  Standard_Real aTol = Precision::Confusion();
+  Standard_Real aMaxTol = Precision::Confusion();
 
-  Standard_Real MinSize = RealLast();
   TopExp_Explorer Explo(theShape, TopAbs_EDGE);
   for (; Explo.More(); Explo.Next())
   {
     const TopoDS_Edge& anEdge = TopoDS::Edge(Explo.Current());
-    Bnd_Box aBox;
-    BRepBndLib::Add(anEdge, aBox);
-    Standard_Real Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
-    aBox.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
-    Standard_Real MaxSize = Max(Xmax - Xmin, Max(Ymax - Ymin, Zmax - Zmin));
-    if (MaxSize < MinSize)
-      MinSize = MaxSize;
+    aMaxTol = Max(aMaxTol, BRep_Tool::Tolerance(anEdge));
   }
 
-  if (!Precision::IsInfinite(MinSize))
-    aTol = 0.1 * MinSize;
-
-  return aTol;
+  return aMaxTol;
 }
 
 //==================================================================================================
index d6ef692308a1190888a64974b3f053528bf820f4..2b281396cd48ffbf6574e0eb6d342f046839072a 100644 (file)
@@ -24,12 +24,14 @@ SET(PROJECT_HEADERS
     GeomData_Point.h
     GeomData_Dir.h
     GeomData_Point2D.h
+    GeomData_Point2DArray.h
 )
 
 SET(PROJECT_SOURCES
     GeomData_Point.cpp
     GeomData_Dir.cpp
     GeomData_Point2D.cpp
+    GeomData_Point2DArray.cpp
 )
 
 SET(PROJECT_LIBRARIES
diff --git a/src/GeomData/GeomData_Point2DArray.cpp b/src/GeomData/GeomData_Point2DArray.cpp
new file mode 100644 (file)
index 0000000..198f916
--- /dev/null
@@ -0,0 +1,120 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "GeomData_Point2DArray.h"
+
+#include <GeomAPI_Pnt2d.h>
+
+#include <ModelAPI_Data.h>
+#include <ModelAPI_Events.h>
+#include <ModelAPI_Expression.h>
+#include <ModelAPI_Feature.h>
+
+#include <cassert>
+
+GeomData_Point2DArray::GeomData_Point2DArray(TDF_Label& theLabel)
+  : myLab(theLabel)
+{
+  reinit();
+}
+
+void GeomData_Point2DArray::reinit()
+{
+  // check the attribute could be already presented in this doc (after load document)
+  myIsInitialized = myLab.FindAttribute(TDataStd_RealArray::GetID(), myArray) == Standard_True;
+}
+
+bool GeomData_Point2DArray::assign(std::shared_ptr<GeomDataAPI_Point2DArray> theOther)
+{
+  std::shared_ptr<GeomData_Point2DArray> anOther =
+      std::dynamic_pointer_cast<GeomData_Point2DArray>(theOther);
+  if (!anOther)
+    return false;
+
+  setSize(anOther->size());
+  myArray->ChangeArray(anOther->myArray->Array(), false);
+  owner()->data()->sendAttributeUpdated(this);
+
+  return true;
+}
+
+int GeomData_Point2DArray::size()
+{
+  if (myArray.IsNull() || !myArray->IsValid()) {
+    // this could be on undo and then redo creation of the attribute
+    // in result creation it may be uninitialized
+    myIsInitialized = myLab.FindAttribute(TDataStd_RealArray::GetID(), myArray) == Standard_True;
+  }
+  // checking the validity because attribute (as a field) may be presented,
+  // but without label: it is undoed
+  return (myArray.IsNull() || !myArray->IsValid()) ? 0 : myArray->Length() / 2;
+}
+
+void GeomData_Point2DArray::setSize(const int theSize)
+{
+  int aValuesSize = 2 * theSize;
+  if (myArray.IsNull() || !myArray->IsValid()) { // create array if it is not done yet
+    if (aValuesSize != 0) { // if size is zero, nothing to do (null array means there is no array)
+      myArray = TDataStd_RealArray::Set(myLab, 0, aValuesSize - 1);
+      owner()->data()->sendAttributeUpdated(this);
+    }
+  }
+  else { // reset the old array
+    if (aValuesSize) {
+      if (aValuesSize != myArray->Length()) { // old data is kept in the new array
+        Handle(TColStd_HArray1OfReal) aNewArray = new TColStd_HArray1OfReal(0, aValuesSize - 1);
+        for (int anIndex = 0; anIndex < aValuesSize && anIndex <= myArray->Upper(); ++anIndex)
+          aNewArray->SetValue(anIndex, myArray->Value(anIndex));
+        myArray->ChangeArray(aNewArray);
+        owner()->data()->sendAttributeUpdated(this);
+      }
+    }
+    else { // size is zero => array must be erased
+      if (!myArray.IsNull()) {
+        myArray.Nullify();
+        myLab.ForgetAttribute(TDataStd_RealArray::GetID());
+        owner()->data()->sendAttributeUpdated(this);
+      }
+    }
+  }
+}
+
+void GeomData_Point2DArray::setPnt(const int theIndex,
+                                   const double theX,
+                                   const double theY)
+{
+  if (myArray->Value(2 * theIndex) != theX || myArray->Value(2 * theIndex + 1) != theY) {
+    myArray->SetValue(2 * theIndex, theX);
+    myArray->SetValue(2 * theIndex + 1, theY);
+    owner()->data()->sendAttributeUpdated(this);
+  }
+}
+
+void GeomData_Point2DArray::setPnt(const int theIndex, const GeomPnt2dPtr& thePoint)
+{
+  setPnt(theIndex, thePoint->x(), thePoint->y());
+}
+
+GeomPnt2dPtr GeomData_Point2DArray::pnt(const int theIndex)
+{
+  GeomPnt2dPtr aPoint;
+  if (theIndex >= 0 && theIndex * 2 < myArray->Length())
+    aPoint.reset(new GeomAPI_Pnt2d(myArray->Value(2 * theIndex), myArray->Value(2 * theIndex + 1)));
+  return aPoint;
+}
diff --git a/src/GeomData/GeomData_Point2DArray.h b/src/GeomData/GeomData_Point2DArray.h
new file mode 100644 (file)
index 0000000..8f3f739
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef GeomData_Point2DArray_H_
+#define GeomData_Point2DArray_H_
+
+#include "GeomData.h"
+
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <TDataStd_RealArray.hxx>
+#include <TDF_Label.hxx>
+
+/** \class GeomData_Point2DArray
+ *  \ingroup DataModel
+ *  \brief Attribute that contains an array of 2D points.
+ */
+class GeomData_Point2DArray : public GeomDataAPI_Point2DArray
+{
+  TDF_Label myLab; ///< the main label of the attribute
+  Handle_TDataStd_RealArray myArray; ///< array that keeps all coordinates of the points
+
+public:
+  /// Copy values from another array
+  /// \return \c true if the copy was successful
+  GEOMDATA_EXPORT virtual bool assign(std::shared_ptr<GeomDataAPI_Point2DArray> theOther);
+
+  /// Returns the size of the array (zero means that it is empty)
+  GEOMDATA_EXPORT virtual int size();
+
+  /// Sets the new size of the array. The previous data is erased.
+  GEOMDATA_EXPORT virtual void setSize(const int theSize);
+
+  /// Defines the value of the array by index [0; size-1]
+  GEOMDATA_EXPORT virtual void setPnt(const int theIndex,
+                                      const double theX, const double theY);
+
+  /// Defines the value of the array by index [0; size-1]
+  GEOMDATA_EXPORT virtual void setPnt(const int theIndex,
+                                      const std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
+
+  /// Returns the value by the index
+  GEOMDATA_EXPORT virtual std::shared_ptr<GeomAPI_Pnt2d> pnt(const int theIndex);
+
+protected:
+  /// Initializes attributes
+  GEOMDATA_EXPORT GeomData_Point2DArray(TDF_Label& theLabel);
+  /// Reinitializes the internal state of the attribute (may be needed on undo/redo, abort, etc)
+  virtual void reinit();
+
+  friend class Model_Data;
+};
+
+#endif
index ecc087c26d2dfbc9722f0fbc67ecb5d0e8fd2b28..fde2672d6975ce651a6c4ed51e2a6264f81e6289 100644 (file)
@@ -27,12 +27,14 @@ SET(PROJECT_HEADERS
     GeomDataAPI_Point.h
     GeomDataAPI_Dir.h
     GeomDataAPI_Point2D.h
+    GeomDataAPI_Point2DArray.h
 )
 
 SET(PROJECT_SOURCES
     GeomDataAPI_Point.cpp
     GeomDataAPI_Dir.cpp
     GeomDataAPI_Point2D.cpp
+    GeomDataAPI_Point2DArray.cpp
 )
 
 SET(PROJECT_LIBRARIES
index 3fd23f6c8a11db58ea4ae7ec33a7e287f1a3fd3b..1a6fd286b46e8ac208e987991ab8f352d9288c25 100644 (file)
 %shared_ptr(GeomDataAPI_Point)
 %shared_ptr(GeomDataAPI_Dir)
 %shared_ptr(GeomDataAPI_Point2D)
+%shared_ptr(GeomDataAPI_Point2DArray)
 
 // all supported interfaces
 %include "GeomDataAPI_Point.h"
 %include "GeomDataAPI_Dir.h"
 %include "GeomDataAPI_Point2D.h"
+%include "GeomDataAPI_Point2DArray.h"
 
 template<class T> std::shared_ptr<T> castTo(std::shared_ptr<ModelAPI_Attribute> theObject);
 %template(geomDataAPI_Point) castTo<GeomDataAPI_Point>;
 %template(geomDataAPI_Dir) castTo<GeomDataAPI_Dir>;
 %template(geomDataAPI_Point2D) castTo<GeomDataAPI_Point2D>;
+%template(geomDataAPI_Point2DArray) castTo<GeomDataAPI_Point2DArray>;
diff --git a/src/GeomDataAPI/GeomDataAPI_Point2DArray.cpp b/src/GeomDataAPI/GeomDataAPI_Point2DArray.cpp
new file mode 100644 (file)
index 0000000..fcd9a8e
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <GeomDataAPI_Point2DArray.h>
+
+std::string GeomDataAPI_Point2DArray::attributeType()
+{
+  return typeId();
+}
+
+GeomDataAPI_Point2DArray::GeomDataAPI_Point2DArray()
+{
+}
+
+GeomDataAPI_Point2DArray::~GeomDataAPI_Point2DArray()
+{
+}
diff --git a/src/GeomDataAPI/GeomDataAPI_Point2DArray.h b/src/GeomDataAPI/GeomDataAPI_Point2DArray.h
new file mode 100644 (file)
index 0000000..1976b16
--- /dev/null
@@ -0,0 +1,74 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef GeomDataAPI_Point2DArray_H_
+#define GeomDataAPI_Point2DArray_H_
+
+#include <GeomDataAPI.h>
+#include <ModelAPI_Attribute.h>
+
+class GeomAPI_Pnt2d;
+
+/**\class GeomDataAPI_Point2DArray
+ * \ingroup DataModel
+ * \brief Attribute that contains array of 2D point coordinates.
+ */
+
+class GeomDataAPI_Point2DArray : public ModelAPI_Attribute
+{
+public:
+  /// Copy values from another array
+  /// \return \c true if the copy was successful
+  GEOMDATAAPI_EXPORT virtual bool assign(std::shared_ptr<GeomDataAPI_Point2DArray> theOther) = 0;
+
+  /// Returns the size of the array (zero means that it is empty)
+  GEOMDATAAPI_EXPORT virtual int size() = 0;
+
+  /// Sets the new size of the array. The previous data is erased.
+  GEOMDATAAPI_EXPORT virtual void setSize(const int theSize) = 0;
+
+  /// Defines the value of the array by index [0; size-1]
+  GEOMDATAAPI_EXPORT virtual void setPnt(const int theIndex,
+                                         const double theX, const double theY) = 0;
+
+  /// Defines the value of the array by index [0; size-1]
+  GEOMDATAAPI_EXPORT virtual void setPnt(const int theIndex,
+                                         const std::shared_ptr<GeomAPI_Pnt2d>& thePoint) = 0;
+
+  /// Returns the value by the index
+  GEOMDATAAPI_EXPORT virtual std::shared_ptr<GeomAPI_Pnt2d> pnt(const int theIndex) = 0;
+
+  /// Returns the type of this class of attributes
+  static std::string typeId()
+  {
+    return std::string("Point2DArray");
+  }
+
+  /// Returns the type of this class of attributes, not static method
+  GEOMDATAAPI_EXPORT virtual std::string attributeType();
+
+protected:
+  /// Objects are created for features automatically
+  GEOMDATAAPI_EXPORT GeomDataAPI_Point2DArray();
+  GEOMDATAAPI_EXPORT virtual ~GeomDataAPI_Point2DArray();
+};
+
+typedef std::shared_ptr<GeomDataAPI_Point2DArray> AttributePoint2DArrayPtr;
+
+#endif
index b23de60169fc52bbaff275f51837ed215f67d7c8..9cd94e9f3229cd77e040c90643243e517e71bd3d 100644 (file)
@@ -28,6 +28,7 @@
   #include "GeomDataAPI_Point.h"
   #include "GeomDataAPI_Dir.h"
   #include "GeomDataAPI_Point2D.h"
+  #include "GeomDataAPI_Point2DArray.h"
 
   #include <memory>
   #include <string>
index db780eac7da4aa4477ff09c591ac2eceaeb68947..5ce42aef320496eaaeb042ad1a6d79f22b616b7e 100644 (file)
 
 void Model_AttributeRefAttrList::append(ObjectPtr theObject)
 {
-  std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(theObject->data());
-  myRef->Append(aData->label().Father());  // store label of the object
+  TDF_Label aLabel;
+  if (theObject) {
+    std::shared_ptr<Model_Data> aData = std::dynamic_pointer_cast<Model_Data>(theObject->data());
+    aLabel = aData->label().Father();
+  }
+
+  myRef->Append(aLabel); // store label of the object
   myIDs->Append(""); // for the object store an empty string
   // do it before the transaction finish to make just created/removed objects know dependencies
   // and reference from composite feature is removed automatically
@@ -306,7 +311,9 @@ void Model_AttributeRefAttrList::remove(const std::set<int>& theIndices)
         myIDs->Append(anIDIter.Value());
       } else { // found, so need to update the dependencies
         aOneisDeleted = true;
-        ObjectPtr anObj = aDoc->objects()->object(aRefIter.Value());
+        ObjectPtr anObj;
+        if (!aRefIter.Value().IsNull())
+          anObj = aDoc->objects()->object(aRefIter.Value());
         if (anObj.get()) {
           REMOVE_BACK_REF(anObj);
         }
index 0b2d2869a3ed7f120f5f1449c405a21d598d94d9..be0964ef5ccaf3fb4f432620472a182303edbfea 100644 (file)
@@ -82,11 +82,13 @@ ObjectPtr Model_AttributeReference::value()
       Handle(TDataStd_AsciiString) anEntry;
       if (myRef->Label().FindAttribute(TDataStd_AsciiString::GetID(), anEntry)) {
         std::shared_ptr<Model_Document> aDR = std::dynamic_pointer_cast<Model_Document>(aRefDoc);
-        TDF_Label aRefLab;
-        TDF_Tool::Label(aDR->objects()->featuresLabel().Data(),
-          anEntry->Get().ToCString(), aRefLab);
-        if (!aRefLab.IsNull()) {
-          return aDR->objects()->object(aRefLab);
+        if (aDR.get() && aDR->objects()) {
+          TDF_Label aRefLab;
+          TDF_Tool::Label(aDR->objects()->featuresLabel().Data(),
+            anEntry->Get().ToCString(), aRefLab);
+          if (!aRefLab.IsNull()) {
+            return aDR->objects()->object(aRefLab);
+          }
         }
       }
     }
index 8ff6d81f8a87b93a8b4a402de30c454f7893cd8b..5c85b0237b5f44592c577b59afa127a7d3a5fbb7 100644 (file)
@@ -185,10 +185,11 @@ bool Model_AttributeSelection::setValue(const ObjectPtr& theContext,
   } else if (theContext->groupName() == ModelAPI_ResultGroup::group()) {
     aSelLab.ForgetAllAttributes(true);
     TDataStd_UAttribute::Set(aSelLab, kSIMPLE_REF_ID);
-  } else { // check the feature context: parent-Part of this feature should not be used
+  } else { // check the feature context: only construction features of PartSet could be selected
     FeaturePtr aFeatureContext = std::dynamic_pointer_cast<ModelAPI_Feature>(theContext);
-    if (aFeatureContext.get()) {
-      if (owner()->document() != aFeatureContext->document()) {
+    if (aFeatureContext.get() && owner()->document() != aFeatureContext->document()) {
+      if (aFeatureContext->results().empty() ||
+          aFeatureContext->firstResult()->groupName() != ModelAPI_ResultConstruction::group()) {
         aSelLab.ForgetAllAttributes(true);
         myRef.setValue(ObjectPtr());
         if (aToUnblock)
@@ -773,19 +774,25 @@ std::string Model_AttributeSelection::namingName(const std::string& theDefaultNa
 
   CenterType aCenterType = NOT_CENTER;
   std::shared_ptr<GeomAPI_Shape> aSubSh = internalValue(aCenterType);
-  ResultPtr aCont = context();
 
-  if (!aCont.get() ||
-      (aCont->groupName() == ModelAPI_ResultConstruction::group() && contextFeature().get())) {
+  FeaturePtr aContFeature = contextFeature();
+  if (aContFeature.get()) {
+    std::string aResName;
+    // checking part-owner
+    if (aContFeature->document() != owner()->document())
+        aResName += aContFeature->document()->kind() + "/";
     // selection of a full feature
-    FeaturePtr aFeatureCont = contextFeature();
-    if (aFeatureCont.get()) {
-      return kWHOLE_FEATURE + aFeatureCont->name();
+    if (aContFeature.get()) {
+      return aResName + kWHOLE_FEATURE + aContFeature->name();
     }
     // in case of selection of removed result
     return "";
   }
 
+  ResultPtr aCont = context();
+  if (!aCont.get()) {
+    return ""; // invalid case
+  }
   TDF_Label aSelLab = selectionLabel();
   if (aSelLab.IsAttribute(kSIMPLE_REF_ID)) { // whole context, no value
     return contextName(aCont);
@@ -868,7 +875,8 @@ void Model_AttributeSelection::selectSubShape(
       if (aPartName == aRootDoc->kind()) {
         aDoc = std::dynamic_pointer_cast<Model_Document>(aRootDoc);
         aSubShapeName = aSubShapeName.substr(aPartEnd + 1);
-      } else {
+      }
+      else {
         ObjectPtr aFound =
           owner()->document()->objectByName(ModelAPI_ResultPart::group(), aPartName);
         if (aFound.get()) { // found such part, so asking it for the name
@@ -887,11 +895,28 @@ void Model_AttributeSelection::selectSubShape(
                 continue;
               std::shared_ptr<GeomAPI_Edge> aSelectedEdge(new GeomAPI_Edge(aSelected));
               setValueCenter(aPart, aSelectedEdge, aCenterType);
-            } else
+            }
+            else
               setValue(aPart, aSelected);
             TDataStd_Integer::Set(selectionLabel(), anIndex);
             return;
           }
+        } else { // for the ImportResult feature Objects widget this may be a result in other part
+       // result may be hidden (like, tranlsatiomn of part) in PartSet, so iterate Part-features
+          int aNum = aRootDoc->size(ModelAPI_Feature::group());
+          for (int a = 0; a < aNum; a++) {
+            FeaturePtr aFeat = std::dynamic_pointer_cast<ModelAPI_Feature>(
+              aRootDoc->object(ModelAPI_Feature::group(), a));
+            if (aFeat.get() && aFeat->data() && aFeat->data()->isValid() &&
+              aFeat->getKind() == "Part" && aFeat->results().size()) {
+              ResultPartPtr aPart =
+                std::dynamic_pointer_cast<ModelAPI_ResultPart>(aFeat->firstResult());
+              if (aPart.get() && aPart->partDoc().get() && aPart->data()->name() == aPartName) {
+                aDoc = std::dynamic_pointer_cast<Model_Document>(aPart->partDoc());
+                aSubShapeName = aSubShapeName.substr(aPartEnd + 1);
+              }
+            }
+          }
         }
       }
     }
@@ -1292,8 +1317,8 @@ void Model_AttributeSelection::computeValues(
 
 
 void Model_AttributeSelection::concealedFeature(
-  const FeaturePtr theFeature, const FeaturePtr theStop, std::list<FeaturePtr>& theConcealers,
-  const ResultPtr theResultOfFeature)
+  const FeaturePtr theFeature, const FeaturePtr theStop, const bool theCheckCopy,
+  std::list<FeaturePtr>& theConcealers, const ResultPtr theResultOfFeature)
 {
   std::set<FeaturePtr> alreadyProcessed;
   alreadyProcessed.insert(theFeature);
@@ -1328,7 +1353,9 @@ void Model_AttributeSelection::concealedFeature(
         if (alreadyProcessed.find(aRefFeat) != alreadyProcessed.end()) // optimization
           continue;
         alreadyProcessed.insert(aRefFeat);
-        if (ModelAPI_Session::get()->validators()->isConcealed(aRefFeat->getKind(), (*aRef)->id()))
+        if (ModelAPI_Session::get()->validators()->isConcealed(aRefFeat->getKind(), (*aRef)->id())
+          || (theCheckCopy &&
+              std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(aRefFeat).get()))
         {
           // for extrusion cut in python script the nested sketch reference may be concealed before
           // it is nested, so, check this composite feature is valid
@@ -1354,7 +1381,8 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
   TDF_Label theAccessLabel,
   std::list<ResultPtr>& theResults, TopTools_ListOfShape& theValShapes)
 {
-  std::set<ResultPtr> aResults; // to avoid duplicates, new context, null if deleted
+  std::list<ResultPtr> aResults; // keep order, new context, null if deleted
+  std::set<ResultPtr> aResultsSet; // to avoid duplicates
   // iterate context and shape, but also if it is sub-shape of main shape, check also it
   TopTools_ListOfShape aContextList;
   aContextList.Append(theContShape);
@@ -1390,32 +1418,42 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
       Handle(TNaming_NamedShape) aNewNS;
       aModifIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNewNS);
       if (aNewNS->Evolution() == TNaming_MODIFY || aNewNS->Evolution() == TNaming_GENERATED) {
-        aResults.insert(aModifierObj);
+        if (aResultsSet.find(aModifierObj) == aResultsSet.end()) {
+          aResultsSet.insert(aModifierObj);
+          aResults.push_back(aModifierObj);
+        }
       } else if (aNewNS->Evolution() == TNaming_DELETE) { // a shape was deleted => result is empty
-        aResults.insert(ResultPtr());
+        aResults.push_back(ResultPtr());
       } else { // not-processed modification => don't support it
         continue;
       }
     }
   }
   // if there exist context composite and sub-result(s), leave only sub(s)
-  for(std::set<ResultPtr>::iterator aResIter = aResults.begin(); aResIter != aResults.end();) {
+  for(std::list<ResultPtr>::iterator aResIter = aResults.begin(); aResIter != aResults.end();) {
     ResultPtr aParent = ModelAPI_Tools::bodyOwner(*aResIter);
     for(; aParent.get(); aParent = ModelAPI_Tools::bodyOwner(aParent))
-      if (aResults.count(aParent))
+      if (aResultsSet.count(aParent))
         break;
-    if (aParent.get()) { // erase from set, so, restart iteration
-      aResults.erase(aParent);
-      aResIter = aResults.begin();
+    if (aParent.get()) {
+      aResultsSet.erase(aParent);
+      for(std::list<ResultPtr>::iterator anIt = aResults.begin(); anIt != aResults.end(); anIt++) {
+        if (*anIt == aParent) {
+          aResults.erase(anIt);
+          aResIter = aResults.begin(); // erase from set, so, restart iteration
+          break;
+        }
+      }
     } else aResIter++;
   }
 
+  bool aStaySame = false;
   if (aResults.empty()) {
     // check the context become concealed by operation which is earlier than this selection
     FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
     FeaturePtr aContextOwner = theDoc->feature(theContext);
     std::list<FeaturePtr> aConcealers;
-    concealedFeature(aContextOwner, aThisFeature, aConcealers, theContext);
+    concealedFeature(aContextOwner, aThisFeature, false, aConcealers, theContext);
     std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
     for(; aConcealer != aConcealers.end(); aConcealer++) {
       std::list<ResultPtr> aRefResults;
@@ -1430,32 +1468,92 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
           continue;
         if (aRefShape->impl<TopoDS_Shape>().IsSame(theContShape)) {
           // add the new context result with the same shape
-          aResults.insert(aRefBody);
+          aResults.push_back(aRefBody);
         }
       }
       if (aResults.empty())
         return true; // feature conceals result, return true, so the context will be removed
     }
-    if (aResults.empty())
-      return false; // no modifications found, must stay the same
+    aStaySame = aResults.empty();
+  }
+  if (myParent && myParent->isMakeCopy()) {
+    // check there are copies before the new results, so, make a copy
+    std::list<ObjectPtr> aCopyContext;
+    std::list<GeomShapePtr> aCopyVals;
+    // features between the new and the old: check the "Move" interface to get a copy
+    FeaturePtr aRootOwner = theDoc->feature(theContext);
+    FeaturePtr anOwner = ModelAPI_Tools::compositeOwner(aRootOwner);
+    for(; anOwner.get(); anOwner = ModelAPI_Tools::compositeOwner(anOwner))
+      aRootOwner = anOwner;
+    FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
+    // iterate all results to find a "Copy" features between the new and one and to add the
+    // copy-results also to results if this attribute refers to the copied shape
+    int anIndex = kUNDEFINED_FEATURE_INDEX;
+    for(FeaturePtr aFeat = theDoc->objects()->nextFeature(aRootOwner, anIndex); aFeat.get() &&
+        aFeat != aThisFeature; aFeat = theDoc->objects()->nextFeature(aFeat, anIndex)) {
+      std::shared_ptr<ModelAPI_FeatureCopyInterface> aCopier =
+        std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(aFeat);
+      if (aCopier.get()) {
+        GeomShapePtr aValShape(new GeomAPI_Shape);
+        aValShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(
+          theValShape.IsNull() ? theContShape : theValShape));
+        aCopier->getCopies(theContext, aValShape, aCopyContext, aCopyVals);
+      }
+    }
+    // check for the further modifications of the copy contexts and values
+    std::list<ObjectPtr>::iterator aCopyContIter = aCopyContext.begin();
+    std::list<GeomShapePtr>::iterator aCopyValIter = aCopyVals.begin();
+    for(; aCopyContIter != aCopyContext.end(); aCopyContIter++, aCopyValIter++) {
+      ResultPtr aNewCont = std::dynamic_pointer_cast<ModelAPI_Result>(*aCopyContIter);
+      TopoDS_Shape aNewContShape = aNewCont->shape()->impl<TopoDS_Shape>();
+      GeomShapePtr aNewVal = *aCopyValIter;
+      TopoDS_Shape aNewValShape;
+      if (aNewVal.get() && !aNewVal->isNull())
+        aNewValShape = aNewVal->impl<TopoDS_Shape>();
+      std::list<ResultPtr> aNewRes;
+      TopTools_ListOfShape aNewUpdatedVal;
+      if (searchNewContext(theDoc, aNewContShape, aNewCont, aNewValShape,
+          theAccessLabel, aNewRes, aNewUpdatedVal)) {
+        // append new results instead of the current ones
+        std::list<ResultPtr>::iterator aNewIter = aNewRes.begin();
+        TopTools_ListIteratorOfListOfShape aNewUpdVal(aNewUpdatedVal);
+        for(; aNewIter != aNewRes.end(); aNewIter++, aNewUpdVal.Next()) {
+          theResults.push_back(*aNewIter);
+          theValShapes.Append(aNewUpdVal.Value());
+        }
+      } else { // the current result is good
+        theResults.push_back(aNewCont);
+        theValShapes.Append(aNewValShape);
+      }
+    }
+    if (aStaySame && !theResults.empty()) { // no changes except copy, so, keep the origin as first
+      theResults.push_front(theContext);
+      theValShapes.Prepend(theValShape);
+      return true;
+    }
   }
+  if (aStaySame)
+    return false;
+
   // iterate all results to find further modifications
-  std::set<ResultPtr>::iterator aResIter = aResults.begin();
-  for(; aResIter != aResults.end(); aResIter++) {
+  std::list<ResultPtr>::iterator aResIter = aResults.begin();
+  for(aResIter = aResults.begin(); aResIter != aResults.end(); aResIter++) {
     if (aResIter->get() != NULL) {
+      ResultPtr aNewResObj = *aResIter;
       // compute new values by two contexts: the old and the new
       TopTools_ListOfShape aValShapes;
-      computeValues(theContext, *aResIter, theValShape, aValShapes);
+      computeValues(theContext, aNewResObj, theValShape, aValShapes);
 
       TopTools_ListIteratorOfListOfShape aNewVal(aValShapes);
       for(; aNewVal.More(); aNewVal.Next()) {
         std::list<ResultPtr> aNewRes;
         TopTools_ListOfShape aNewUpdatedVal;
         TopoDS_Shape aNewValSh = aNewVal.Value();
-        TopoDS_Shape aNewContShape = (*aResIter)->shape()->impl<TopoDS_Shape>();
+        TopoDS_Shape aNewContShape = aNewResObj->shape()->impl<TopoDS_Shape>();
+
         if (theValShape.IsNull() && aNewContShape.IsSame(aNewValSh))
           aNewValSh.Nullify();
-        if (searchNewContext(theDoc, aNewContShape, *aResIter, aNewValSh,
+        if (searchNewContext(theDoc, aNewContShape, aNewResObj, aNewValSh,
                              theAccessLabel, aNewRes, aNewUpdatedVal))
         {
           // append new results instead of the current ones
@@ -1466,7 +1564,7 @@ bool Model_AttributeSelection::searchNewContext(std::shared_ptr<Model_Document>
             theValShapes.Append(aNewUpdVal.Value());
           }
         } else { // the current result is good
-          theResults.push_back(*aResIter);
+          theResults.push_back(aNewResObj);
           theValShapes.Append(aNewValSh);
         }
       }
@@ -1488,25 +1586,40 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
       if (aFeature.get()) {
         FeaturePtr aThisFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(owner());
         std::list<FeaturePtr> aConcealers;
-        concealedFeature(aFeature, aThisFeature, aConcealers, ResultPtr());
+        bool aCopyPossible = myParent && myParent->isMakeCopy();
+        concealedFeature(aFeature, aThisFeature, aCopyPossible, aConcealers, ResultPtr());
         if (aConcealers.empty())
           return;
+        // if there are copies, but no direct modification, keep the original
+        bool aKeepOrigin = false;
+        if (aCopyPossible) {
+          std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
+          for(aKeepOrigin = true; aConcealer != aConcealers.end(); aConcealer++)
+            if (!std::dynamic_pointer_cast<ModelAPI_FeatureCopyInterface>(*aConcealer).get()) {
+              aKeepOrigin = false;
+              break;
+            }
+          if (aKeepOrigin) {
+            aConcealers.push_front(aFeature);
+          }
+        }
         bool aChanged = false;
         std::list<FeaturePtr>::iterator aConcealer = aConcealers.begin();
         for(; aConcealer != aConcealers.end(); aConcealer++)
-          if (!myParent->isInList(*aConcealer, anEmptyShape)) {// avoid addition of duplicates
-            setValue(*aConcealer, anEmptyShape);
-            aChanged = true;
-          }
-        if (aConcealer == aConcealers.end()) {
-          if (!aChanged) // remove this
-            theRemove = true;
-        } else { // append new
-          for(aConcealer++; aConcealer != aConcealers.end(); aConcealer++)
-            if (!myParent->isInList(*aConcealer, anEmptyShape)) // avoid addition of duplicates
+          if (aChanged) {
+            if (aKeepOrigin || !myParent->isInList(*aConcealer, anEmptyShape))
               myParent->append(*aConcealer, anEmptyShape);
-        }
-        if (aChanged) // searching for the further modifications
+          } else {
+            if (!myParent->isInList(*aConcealer, anEmptyShape)) {// avoid addition of duplicates
+              setValue(*aConcealer, anEmptyShape);
+              aChanged = true;
+            } else if (aCopyPossible && *aConcealer == aFeature) {// keep origin in case of copy
+              aChanged = true;
+            }
+          }
+        if (!aChanged) // remove this
+          theRemove = true;
+        else if (!aKeepOrigin) // searching further modifications only if current changed
           updateInHistory(theRemove);
       }
     }
@@ -1704,6 +1817,9 @@ void Model_AttributeSelection::updateInHistory(bool& theRemove)
         if (!myParent || !myParent->isInList(aNewContext, aValueShape)) { // avoid duplicates
           setValue(aNewContext, aValueShape);
           aFirst = false;
+        } else if (aNewContext == aContext && myParent && myParent->isMakeCopy()) {
+          // this may be exactly the old one, not modified in case of copy
+          aFirst = false;
         }
       } else if (myParent) {
         if (!myParent->isInList(aNewContext, aValueShape)) // avoid addition of duplicates
index 50a7ddbb91645726c340b095ce6db0ac97e3acd8..8e70cb28b3f595ea9c10f01c6b2fed92dc9fc296 100644 (file)
@@ -208,8 +208,8 @@ protected:
   /// Returns features that conceals theFeature and located in history before theStop
   /// theResultOfFeature if not null defines exact referenced result of a feature
   void concealedFeature(
-    const FeaturePtr theFeature, const FeaturePtr theStop, std::list<FeaturePtr>& theConcealers,
-    const ResultPtr theResultOfFeature);
+    const FeaturePtr theFeature, const FeaturePtr theStop, const bool theCheckCopy,
+    std::list<FeaturePtr>& theConcealers, const ResultPtr theResultOfFeature);
 
   friend class Model_Data;
   friend class Model_AttributeSelectionList;
index 170190ef66879e1e44f82e71e4be35340ea4f2ca..9e7b0b6ff1144cf9957a4cf6599808398844e791 100644 (file)
@@ -269,6 +269,16 @@ void Model_AttributeSelectionList::remove(const std::set<int>& theIndices)
   }
 }
 
+void Model_AttributeSelectionList::copyTo(AttributeSelectionListPtr theTarget) const
+{
+  std::shared_ptr<Model_AttributeSelectionList> aTarget =
+    std::dynamic_pointer_cast<Model_AttributeSelectionList>(theTarget);
+  if (aTarget) {
+    copyAttrs(myLab, aTarget->myLab);
+    aTarget->reinit();
+  }
+}
+
 int Model_AttributeSelectionList::size()
 {
   return mySize->Get();
index 3ed3e2804aca7e396befd8f711a858dd1829a9d2..2db50d9b400f5b49254550a7493886c7bcbd24ec 100644 (file)
@@ -66,6 +66,9 @@ public:
   MODEL_EXPORT virtual void append(const std::string& theType, const std::string& theContextName,
                                    const int theIndex);
 
+  /// Copy the selection list to the destination attribute
+  MODEL_EXPORT virtual void copyTo(AttributeSelectionListPtr theTarget) const;
+
   /// Reset temporary stored values
   virtual void removeTemporaryValues();
 
index e5641930e1b1253ab05e0a83f5afbd1ab8c34137..2a25c5136913462dd8887e5249277880e80ac42a 100644 (file)
 
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 
 #include <GeomData_Point.h>
 #include <GeomData_Point2D.h>
+#include <GeomData_Point2DArray.h>
 #include <GeomData_Dir.h>
 #include <Events_Loop.h>
 #include <Events_InfoMessage.h>
@@ -217,7 +219,10 @@ AttributePtr Model_Data::addAttribute(
     }
     anAttribute->myIsInitialized = anAllInitialized;
     anAttr = anAttribute;
+  } else if (theAttrType == GeomData_Point2DArray::typeId()) {
+    anAttr = new GeomData_Point2DArray(anAttrLab);
   }
+
   if (anAttr) {
     aResult = std::shared_ptr<ModelAPI_Attribute>(anAttr);
     myAttrs[theID] = std::pair<AttributePtr, int>(aResult, anAttrIndex);
index 79162a77945b2302e00d7ddc8818358b7cd60457..1efd395afc2519c15626f4bd9d77bdc561af615c 100644 (file)
@@ -1263,6 +1263,12 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis, c
     } while (aSub.get());
   }
 
+  AttributeSelectionListPtr aMovedList;
+  if (theMoved->getKind() == "Group") {
+    aMovedList = theMoved->selectionList("group_list");
+    if (aMovedList.get())
+      aMovedList->setMakeCopy(true);
+  }
   myObjs->moveFeature(theMoved, anAfterThisSub);
 
   if (theSplit) { // split the group into sub-features
@@ -1275,10 +1281,8 @@ void Model_Document::moveFeature(FeaturePtr theMoved, FeaturePtr theAfterThis, c
     // must be after move to make enabled all features which are before theMoved
     setCurrentFeature(theMoved, true);
   }
-
-  if (theSplit) { // split the group into sub-features
-    theMoved->customAction("split");
-  }
+  if (aMovedList.get())
+    aMovedList->setMakeCopy(false);
 }
 
 void Model_Document::updateHistory(const std::shared_ptr<ModelAPI_Object> theObject)
index 152aaaeb84ee27a8c4211c1c3d3618dae7221f50..047a4c10586ec96004bdfa7487a6aa89d15ecceb 100644 (file)
@@ -1490,17 +1490,20 @@ std::shared_ptr<ModelAPI_Folder> Model_Objects::findFolder(
     if (theBelow)
       continue;
 
-    // if feature is in sub-component, skip it
-    FeaturePtr aCurFeature = feature(aCurLabel);
-    if (isSkippedFeature(aCurFeature))
-      continue;
-
+    // issue #18733: check for the last feature in folder before checking the sub-feature,
+    //               because the folder may end by the feature which is
+    //               neither a sub-feature nor a feature in history.
     if (!aLastFeatureInFolder.IsNull()) {
       if (IsEqual(aCurLabel, aLastFeatureInFolder))
         aLastFeatureInFolder.Nullify(); // the last feature in the folder is achieved
       continue;
     }
 
+    // if feature is in sub-component, skip it
+    FeaturePtr aCurFeature = feature(aCurLabel);
+    if (isSkippedFeature(aCurFeature))
+      continue;
+
     const ObjectPtr& aFolderObj = folder(aCurLabel);
     if (aFolderObj.get()) {
       aFoundFolder = std::dynamic_pointer_cast<ModelAPI_Folder>(aFolderObj);
index c5bedb6bae96d0c57bbaea334ff4e774b974df0c..4d19917385d6b25111e39640d112c97475bee7fe 100644 (file)
@@ -113,6 +113,21 @@ void Model_ResultBody::loadModifiedShapes(const std::shared_ptr<GeomAlgoAPI_Make
   }
 }
 
+void Model_ResultBody::loadFirstLevel(GeomShapePtr theShape, const std::string& theName)
+{
+  if (mySubs.size()) { // consists of subs
+    for (std::vector<ResultBodyPtr>::const_iterator aSubIter = mySubs.cbegin();
+      aSubIter != mySubs.cend();
+      ++aSubIter)
+    {
+      const ResultBodyPtr& aSub = *aSubIter;
+      aSub->loadFirstLevel(theShape, theName);
+    }
+  } else { // do for this directly
+    myBuilder->loadFirstLevel(theShape, theName);
+  }
+}
+
 int Model_ResultBody::numberOfSubs(bool forTree) const
 {
   return int(mySubs.size());
index 663bf8cecf416c54e02ed6f4e8ace29f3f719672..d0a1f908e68c946dbf38b6859de94cb8326ef33a 100644 (file)
@@ -78,6 +78,8 @@ public:
                                   const GeomAPI_Shape::ShapeType theShapeTypeToExplore,
                                   const std::string& theName = "") override;
 
+  /// load shapes of the first level (to be used during shape import)
+  MODEL_EXPORT virtual void loadFirstLevel(GeomShapePtr theShape, const std::string& theName);
 
   /// Returns the number of sub-elements
   MODEL_EXPORT virtual int numberOfSubs(bool forTree = false) const;
index 7bbadba0dc02d707400574092a5cd23bdafb39df..f89eff7d738ec5632febcd3f19e9f44ff4b2046a 100644 (file)
@@ -89,7 +89,8 @@ void Model_ResultPart::activate()
   SessionPtr aMgr = ModelAPI_Session::get();
   if (!aMgr->isOperation()) {
     // open transaction even document is not created to set current docs in setActiveDocument
-    aMgr->startOperation("Activation");
+    std::string aMsg = "Activation " + data()->name();
+    aMgr->startOperation(aMsg);
     isNewTransaction = true;
   }
   if (!aDocRef->value().get()) {  // create (or open) a document if it is not yet created
@@ -113,6 +114,22 @@ void Model_ResultPart::activate()
   }
 }
 
+
+void Model_ResultPart::loadPart()
+{
+  std::shared_ptr<ModelAPI_AttributeDocRef> aDocRef = data()->document(DOC_REF());
+  if (!aDocRef->value().get()) {  // create (or open) a document if it is not yet created
+    Handle(Model_Application) anApp = Model_Application::getApplication();
+    if (anApp->isLoadByDemand(data()->name(), aDocRef->docId())) {
+      anApp->loadDocument(data()->name(), aDocRef->docId()); // if it is just new part, load fails
+    }
+    else {
+      anApp->createDocument(aDocRef->docId());
+    }
+  }
+}
+
+
 std::shared_ptr<ModelAPI_ResultPart> Model_ResultPart::original()
 {
   if (myTrsf.get() && baseRef().get()) {  // the second condition is due to #2035
index fff44ed1c04531e540200fe8d6391d9b2777e916..0f27e9c589cf91b55e358f12e4881d3d0e04c293 100644 (file)
@@ -95,6 +95,9 @@ class Model_ResultPart : public ModelAPI_ResultPart
   /// Returns the shape selected in the selection index
   MODEL_EXPORT virtual std::shared_ptr<GeomAPI_Shape> selectionValue(const int theIndex);
 
+  /// Loading the part from file
+  MODEL_EXPORT virtual void loadPart();
+
 protected:
   /// makes a result on a temporary feature (an action)
   Model_ResultPart();
index d8896e68039a01774be036a7720ca08e9a9b1f8e..09a56b18841be280804691a95982130a7ea5da6f 100644 (file)
@@ -535,9 +535,21 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
       return false;
   }
 
+  // check this feature is not yet checked or processed
+  bool aIsModified = myModified.find(theFeature) != myModified.end();
+  if (!aIsModified && myIsFinish) { // get info about the modification for features without preview
+    if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
+      aIsModified = true;
+      std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
+      // contains itself, so, we don't know which was the reason and the reason is any
+      aNewSet.insert(theFeature);
+      myModified[theFeature] = aNewSet;
+    }
+  }
+
   if (myProcessed.find(theFeature) == myProcessed.end()) {
     myProcessed[theFeature] = 0;
-  } else {
+  } else if (aIsModified) {
     int aCount = myProcessed[theFeature];
     if (aCount > 100) {
       // too many repetition of processing (in VS it may crash on 330 with stack overflow)
@@ -550,18 +562,6 @@ bool Model_Update::processFeature(FeaturePtr theFeature)
     myProcessed[theFeature] = aCount + 1;
   }
 
-  // check this feature is not yet checked or processed
-  bool aIsModified = myModified.find(theFeature) != myModified.end();
-  if (!aIsModified && myIsFinish) { // get info about the modification for features without preview
-    if (theFeature->data()->execState() == ModelAPI_StateMustBeUpdated) {
-      aIsModified = true;
-      std::set<std::shared_ptr<ModelAPI_Feature> > aNewSet;
-      // contains itself, so, we don't know which was the reason and the reason is any
-      aNewSet.insert(theFeature);
-      myModified[theFeature] = aNewSet;
-    }
-  }
-
 #ifdef DEB_UPDATE
     std::cout<<"* Process feature "<<theFeature->name()<<std::endl;
 #endif
index a1894001e694def50b1455874b06ddf7c73ec1db..6df21ce5b1399a6c2d51103efd22ffe54de47511 100644 (file)
@@ -254,4 +254,5 @@ ADD_UNIT_TESTS(TestConstants.py
                Test2901.py
                Test2903.py
                Test3020.py
+               Test3116.py
 )
index 913b41b8bebc172ab07dbef5f135e83ab0ed2b4d..d0f093813b9a422003990c9a72319907b416b4b1 100644 (file)
@@ -218,8 +218,5 @@ template<class T1, class T2> std::shared_ptr<T1> shared_ptr_cast(std::shared_ptr
 %template(PointList) std::list<std::shared_ptr<GeomAPI_Pnt> >;
 %template(PointSet) std::set<std::shared_ptr<GeomAPI_Pnt> >;
 
-// Geometry casts
-%template(shapeToEdge) shared_ptr_cast<GeomAPI_Edge, GeomAPI_Shape>;
-
 template<class T1, class T2> std::shared_ptr<T1> shared_ptr_cast(std::shared_ptr<T2> theObject);
 %template(featureToPresentation) shared_ptr_cast<GeomAPI_IPresentable, ModelAPI_Feature>;
index a88017e3412403701017112c1a31277e6e17f0f8..16cd5f97981514ce40dbfae7fe18e50d94c438be 100644 (file)
@@ -39,7 +39,10 @@ class ModelAPI_AttributeSelectionList : public ModelAPI_Attribute
   /// may be sub-objects, so, it is the same as all sub-shapes are selected (#3005). It is "false"
   /// by default.
   bool myIsWholeResultAllowed;
- public:
+  /// Flag that indicates that update in history must check the copy-features
+  /// and make a copy of selection for them.
+  bool myMakeCopy;
+public:
   /// Adds the new reference to the end of the list
   /// \param theContext object where the sub-shape was selected
   /// \param theSubShape selected sub-shape (if null, the whole context is selected)
@@ -61,6 +64,9 @@ class ModelAPI_AttributeSelectionList : public ModelAPI_Attribute
   virtual void append(const std::string& theType, const std::string& theContextName,
                       const int theIndex) = 0;
 
+  /// Copy the selection list to the destination attribute
+  virtual void copyTo(std::shared_ptr<ModelAPI_AttributeSelectionList> theTarget) const = 0;
+
   /// Reset temporary stored values
   virtual void removeTemporaryValues() = 0;
 
@@ -135,10 +141,20 @@ class ModelAPI_AttributeSelectionList : public ModelAPI_Attribute
     myIsWholeResultAllowed = theFlag;
   }
 
+  /// Returns true if a copy features must be used in update in history.
+  MODELAPI_EXPORT virtual const bool isMakeCopy() const {
+    return myMakeCopy;
+  }
+
+  /// Sets true if a copy features must be used in update in history.
+  MODELAPI_EXPORT virtual void setMakeCopy(const bool theFlag)  {
+    myMakeCopy = theFlag;
+  }
+
 protected:
   /// Default constructor
   MODELAPI_EXPORT ModelAPI_AttributeSelectionList() : ModelAPI_Attribute()
-  {myIsWholeResultAllowed = false;}
+  {myIsWholeResultAllowed = false; myMakeCopy = false;}
 
 };
 
index f1bcd1e26707e11949f962c8a4b94f87c7236da8..92474383c6e743179f0c96e25b445fe2cfa95977 100644 (file)
@@ -335,10 +335,12 @@ void ModelAPI_ObjectMovedMessage::setMovedObject(const ObjectPtr& theMovedObject
   myMovedAttribute = AttributePtr();
 }
 
-void ModelAPI_ObjectMovedMessage::setMovedAttribute(const AttributePtr& theMovedAttribute)
+void ModelAPI_ObjectMovedMessage::setMovedAttribute(const AttributePtr& theMovedAttribute,
+                                                    const int thePointIndex)
 {
   myMovedAttribute = theMovedAttribute;
   myMovedObject = ObjectPtr();
+  myMovedPointIndex = thePointIndex;
 }
 
 void ModelAPI_ObjectMovedMessage::setOriginalPosition(double theX, double theY)
index 0b250ebd53e24493c8f0b4b93e42841d71b53aa4..02fdadb0936d97a68e6167a9aa5e4476b4364ac1 100644 (file)
@@ -486,6 +486,7 @@ class ModelAPI_ObjectMovedMessage : public Events_Message
 {
   ObjectPtr myMovedObject;
   AttributePtr myMovedAttribute;
+  int myMovedPointIndex;
 
   std::shared_ptr<GeomAPI_Pnt2d> myOriginalPosition;
   std::shared_ptr<GeomAPI_Pnt2d> myCurrentPosition;
@@ -496,7 +497,10 @@ public:
   /// Set object which is being moved (if the message already contains attribute it will be cleared)
   MODELAPI_EXPORT void setMovedObject(const ObjectPtr& theMovedObject);
   /// Set attribute which is being moved (if the message already contains object it will be cleared)
-  MODELAPI_EXPORT void setMovedAttribute(const AttributePtr& theMovedAttribute);
+  /// \param[in] theMovedAttribute moved attribute
+  /// \param[in] thePointIndex     index of the point if the moved attribute is an array of points
+  MODELAPI_EXPORT void setMovedAttribute(const AttributePtr& theMovedAttribute,
+                                         const int thePointIndex = -1);
 
   /// Return moved object
   ObjectPtr movedObject() const
@@ -504,6 +508,9 @@ public:
   /// Return moved attribute
   AttributePtr movedAttribute() const
   { return myMovedAttribute; }
+  /// Return index of the moved point
+  int movedPointIndex() const
+  { return myMovedPointIndex; }
 
   /// Set original mouse position
   MODELAPI_EXPORT void setOriginalPosition(double theX, double theY);
index fe7cc35e872465e157331fc81353a5416ecfb5ad..8c231608656b13658eb1334ce373d715f557adbc 100644 (file)
@@ -250,5 +250,17 @@ class ModelAPI_Feature : public ModelAPI_Object
 //! Pointer on feature object
 typedef std::shared_ptr<ModelAPI_Feature> FeaturePtr;
 
-#endif
+//! An interface for performing special copy actions. To give feature which is moved (a group)
+//! over this feature.
+class ModelAPI_FeatureCopyInterface {
+public:
+  /// An algorithm to update the moved feature by the separate Copy feature
+  /// \param theContext the original context object
+  /// \param theValue the original shape
+  /// \param theCopies resulting copy-context will be appended here
+  virtual void getCopies(ObjectPtr theContext, std::shared_ptr<GeomAPI_Shape> theValue,
+                         std::list<ObjectPtr>& theCopyContext,
+                         std::list<std::shared_ptr<GeomAPI_Shape> >& theCopyVals) = 0;
+};
 
+#endif
index 603aaac4df93d98a740c804eefbafa7a4928d40d..0a291ae486bd13ed486ae86ba8558e60faeec8dd 100644 (file)
@@ -139,12 +139,6 @@ void ModelAPI_ResultBody::loadDeletedShapes(const GeomMakeShapePtr& theAlgo,
   myBuilder->loadDeletedShapes(theAlgo, theOldShape, theShapeTypeToExplore, theShapesToExclude);
 }
 
-void ModelAPI_ResultBody::loadFirstLevel(GeomShapePtr theShape,
-                                         const std::string& theName)
-{
-  myBuilder->loadFirstLevel(theShape, theName);
-}
-
 // LCOV_EXCL_START
 bool ModelAPI_ResultBody::isConnectedTopology()
 {
index c585280cc4c4c0e9ca30c32921aad5d5af7c6c74..7b9b3177871ffff939626c90df9b938758e107bf 100644 (file)
@@ -162,7 +162,7 @@ public:
 
   /// load shapes of the first level (to be used during shape import)
   MODELAPI_EXPORT virtual void loadFirstLevel(GeomShapePtr theShape,
-                                              const std::string& theName);
+                                              const std::string& theName) = 0;
 
   /// Returns true is the topology is connected.
   MODELAPI_EXPORT virtual bool isConnectedTopology() = 0;
index 4a4756be5c732fe809d452c238f0529e99ca8111..0ceb0865213f1bc03393d288ac1a985e4e9551b0 100644 (file)
@@ -97,6 +97,9 @@ class ModelAPI_ResultPart : public ModelAPI_Result
 
   /// Updates the shape-result of the part (called on Part feature execution)
   virtual void updateShape() = 0;
+
+  /// Loading the part from file
+  virtual void loadPart() = 0;
 };
 
 //! Pointer on feature object
index 54fa5488d558f913289bad9707bbdebe9486d284..280281161658dff410af88e17ccdbc3f5677f53d 100644 (file)
@@ -645,6 +645,7 @@ std::pair<std::string, bool> getDefaultName(const std::shared_ptr<ModelAPI_Resul
         // check the referred object is a Body
         // (for example, ExtrusionCut has a sketch as a first attribute which is concealing)
         bool isBody = aRefIt->second.size() > 1 || (aRefIt->second.size() == 1 &&
+                      aRefIt->second.front().get() &&
                       aRefIt->second.front()->groupName() == ModelAPI_ResultBody::group());
         if (isBody && (isMainArg || aFoundRef == aReferences.end() ||
             aData->isPrecedingAttribute(aRefIt->first, aFoundRef->first)))
index de4312c62408c486f42c0d6e4831818e82c0cfd0..0c4f99e0367fe82f98ce22dd3d28139b6bf6a064 100644 (file)
   #include <memory>
   #include <string>
 
-  template<class T1, class T2>
-  std::shared_ptr<T1> shared_ptr_cast(std::shared_ptr<T2> theObject)
-  {
-    return std::dynamic_pointer_cast<T1>(theObject);
-  }
-
 #endif /* SRC_MODELAPI_MODELAPI_SWIG_H_ */
diff --git a/src/ModelAPI/Test/Test3116.py b/src/ModelAPI/Test/Test3116.py
new file mode 100644 (file)
index 0000000..2540307
--- /dev/null
@@ -0,0 +1,39 @@
+# 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
+#
+
+# Cheching for selection of the whole feature (Sketch) in PartSet from the Part
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY"))
+SketchCircle_1 = Sketch_1.addCircle(16, 15, 3)
+model.do()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Edge_1 = model.addEdge(Part_1_doc, [model.selection("COMPOUND", "PartSet/all-in-Sketch_1")], False)
+model.end()
+
+# check the result validity
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+assert(aFactory.validate(Edge_1.feature()))
+
+assert(model.checkPythonDump())
index 04786ff11a4736408f99bcb87ea79427b94f60c6..41a55586c303483fbe86e9fb2e063b9243c909f3 100644 (file)
@@ -122,4 +122,5 @@ ADD_UNIT_TESTS(
   TestDeflectionDump.py
   TestUndoRedo.py
   Test2488.py
+  Test18451.py
 )
index 72971cc015fdb391b1226a44f2c303097ff74f1d..30fb144b34a2ac9c177abcfb15c99bc4067aa1d0 100644 (file)
   }
 }
 
+
+%typemap(in) const std::list<int> & (std::list<int> temp) {
+  int newmem = 0;
+  if (PySequence_Check($input)) {
+    for (Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+      if (PyLong_Check(item)) {
+        temp.push_back((int)PyLong_AsLong(item));
+      } else {
+        PyErr_SetString(PyExc_TypeError, "argument must integet value.");
+        return NULL;
+      }
+      Py_DECREF(item);
+    }
+    $1 = &temp;
+  } else {
+    PyErr_SetString(PyExc_ValueError, "argument must be a tuple of integer values.");
+    return NULL;
+  }
+}
+
+%typecheck(SWIG_TYPECHECK_POINTER) std::list<int>, const std::list<int>& {
+  int newmem = 0;
+  if (PySequence_Check($input)) {
+    for (Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+      if (PyLong_Check(item)) {
+        $1 = 1;
+      } else {
+        $1 = 0;
+        break;
+      }
+      Py_DECREF(item);
+    }
+  } else {
+    $1 = 0;
+  }
+}
+
+
 %typemap(in) const std::list<double> & (std::list<double> temp) {
-  double * temp_attribute;
   int newmem = 0;
   if (PyTuple_Check($input)) {
     for (Py_ssize_t i = 0; i < PyTuple_Size($input); ++i) {
 }
 
 %typecheck(SWIG_TYPECHECK_POINTER) std::list<double>, const std::list<double>& {
-  double * temp_object;
-  std::shared_ptr<ModelHighAPI_Interface> * temp_interface;
   int newmem = 0;
   if (PyTuple_Check($input)) {
     for (Py_ssize_t i = 0; i < PyTuple_Size($input); ++i) {
   }
 }
 
+
+%typemap(in) const std::list<ModelHighAPI_Double> & (std::list<ModelHighAPI_Double> temp) {
+  ModelHighAPI_Double * temp_double;
+  if (PySequence_Check($input)) {
+    for (Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+      if (PyFloat_Check(item) || PyLong_Check(item)) {
+        temp.push_back(ModelHighAPI_Double(PyFloat_AsDouble(item)));
+      } else if (PyUnicode_Check(item)) {
+        temp.push_back(ModelHighAPI_Double(PyUnicode_AsUTF8(item)));
+      } else if ((SWIG_ConvertPtr(item, (void **)&temp_double, $1_descriptor, SWIG_POINTER_EXCEPTION)) == 0) {
+        temp.push_back(*temp_double);
+      } else {
+        PyErr_SetString(PyExc_ValueError, "argument must be a list of ModelHighAPI_Double, float, int or string.");
+        return NULL;
+      }
+      Py_DECREF(item);
+    }
+    $1 = &temp;
+  } else {
+    PyErr_SetString(PyExc_ValueError, "argument must be a list of ModelHighAPI_Double, float, int or string.");
+    return NULL;
+  }
+}
+
+%typecheck(SWIG_TYPECHECK_POINTER) std::list<ModelHighAPI_Double>, const std::list<ModelHighAPI_Double> & {
+  if (PySequence_Check($input)) {
+    $1 = 1;
+    for (Py_ssize_t i = 0; i < PySequence_Size($input) && $1; ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+      $1 = ((PyFloat_Check(item) || PyLong_Check(item) || PyUnicode_Check(item)) && !PyBool_Check(item)) ? 1 : 0;
+      Py_DECREF(item);
+    }
+  } else {
+    $1 = 0;
+  }
+}
+
+
+%typemap(in) const std::list<ModelHighAPI_Integer> & (std::list<ModelHighAPI_Integer> temp) {
+  ModelHighAPI_Integer * temp_int;
+  if (PySequence_Check($input)) {
+    for (Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+      if (PyLong_Check(item)) {
+        temp.push_back(ModelHighAPI_Integer(PyLong_AsLong(item)));
+      } else if (PyUnicode_Check(item)) {
+        temp.push_back(ModelHighAPI_Integer(PyUnicode_AsUTF8(item)));
+      } else if ((SWIG_ConvertPtr(item, (void **)&temp_int, $1_descriptor, SWIG_POINTER_EXCEPTION)) == 0) {
+        temp.push_back(*temp_int);
+      } else {
+        PyErr_SetString(PyExc_ValueError, "argument must be a list of ModelHighAPI_Integer, int or string.");
+        return NULL;
+      }
+      Py_DECREF(item);
+    }
+    $1 = &temp;
+  } else {
+    PyErr_SetString(PyExc_ValueError, "argument must be a list of ModelHighAPI_Integer, int or string.");
+    return NULL;
+  }
+}
+
+%typecheck(SWIG_TYPECHECK_POINTER) std::list<ModelHighAPI_Integer>, const std::list<ModelHighAPI_Integer> & {
+  if (PySequence_Check($input)) {
+    $1 = 1;
+    for (Py_ssize_t i = 0; i < PySequence_Size($input) && $1; ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+      $1 = ((PyLong_Check(item) || PyUnicode_Check(item)) && !PyBool_Check(item)) ? 1 : 0;
+      Py_DECREF(item);
+    }
+  } else {
+    $1 = 0;
+  }
+}
+
+
 // all supported interfaces
 %include "ModelHighAPI_Double.h"
 %include "ModelHighAPI_Dumper.h"
index 35370c8190295fb7bcbdc02229ce90c1c06dafd6..33c52647d98ef1009250ab14075a844c7f387869 100644 (file)
@@ -46,6 +46,12 @@ ModelHighAPI_Double::~ModelHighAPI_Double()
 {
 }
 
+double ModelHighAPI_Double::value() const
+{
+  // needed for array of double, which supports no text
+  return myDouble;
+}
+
 //--------------------------------------------------------------------------------------
 void ModelHighAPI_Double::fillAttribute(
     const std::shared_ptr<ModelAPI_AttributeDouble> & theAttribute) const
index 6861a7c0a3f55b9a5898acbc3ea61556841f541b..9fe5df5369c272b49df1379bd0ba261fd5739f10 100644 (file)
@@ -60,6 +60,9 @@ public:
     const ModelHighAPI_Double & theY,
     const ModelHighAPI_Double & theZ) const;
 
+  /// Value of the attribute
+  MODELHIGHAPI_EXPORT double value() const;
+
 private:
   enum VariantType { VT_DOUBLE, VT_STRING } myVariantType;
   double myDouble;
index 577f8b2db30f9203a75093423792f5a70f3cf6fc..778b1c94c7c4573a139af74896bc2a929ea577f8 100644 (file)
@@ -22,6 +22,7 @@
 #include <Config_PropManager.h>
 
 #include <GeomAPI_Pnt.h>
+#include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_Dir.h>
 #include <GeomAPI_ShapeExplorer.h>
 #include <GeomAPI_ShapeIterator.h>
 #include <GeomDataAPI_Dir.h>
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 
 #include <ModelAPI_AttributeBoolean.h>
 #include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
 #include <ModelAPI_AttributeIntArray.h>
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeRefAttr.h>
@@ -1081,6 +1084,43 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
   return *this;
 }
 
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+  const std::shared_ptr<GeomDataAPI_Point2DArray>& thePointArray)
+{
+  static const int aThreshold = 4;
+  static bool aDumpAsIs = false;
+  static std::string aSeparator = "";
+  // if number of elements in the list if greater than a threshold,
+  // dump it in a separate line with specific name
+  int aSize = thePointArray->size();
+  if (aDumpAsIs || aSize <= aThreshold) {
+    *myDumpStorage << "[";
+    GeomPnt2dPtr aPoint = thePointArray->pnt(0);
+    *myDumpStorage << "(" << aPoint->x() << ", " << aPoint->y() << ")";
+    for (int anIndex = 1; anIndex < aSize; ++anIndex) {
+      aPoint = thePointArray->pnt(anIndex);
+      *myDumpStorage << "," << aSeparator << " (" << aPoint->x() << ", " << aPoint->y() << ")";
+    }
+    *myDumpStorage << aSeparator << "]";
+  }
+  else {
+    // name of list
+    FeaturePtr anOwner = ModelAPI_Feature::feature(thePointArray->owner());
+    std::string aListName = name(anOwner) + "_" + thePointArray->id();
+    // reserve dumped buffer and store list "as is"
+    myDumpStorage->reserveBuffer();
+    aDumpAsIs = true;
+    aSeparator = std::string("\n") + std::string(aListName.size() + 3, ' ');
+    *this << aListName << " = " << thePointArray << "\n";
+    aDumpAsIs = false;
+    aSeparator = "";
+    // append reserved data to the end of the current buffer
+    myDumpStorage->restoreReservedBuffer();
+    *myDumpStorage << aListName;
+  }
+  return *this;
+}
+
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeBoolean>& theAttrBool)
 {
@@ -1099,6 +1139,20 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
   return *this;
 }
 
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+    const std::shared_ptr<ModelAPI_AttributeIntArray>& theArray)
+{
+  *myDumpStorage << "[";
+  int aSize = theArray->size();
+  if (aSize > 0) {
+    *myDumpStorage << theArray->value(0);
+    for (int anIndex = 1; anIndex < aSize; ++anIndex)
+      *myDumpStorage << ", " << theArray->value(anIndex);
+  }
+  *myDumpStorage << "]";
+  return *this;
+}
+
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal)
 {
@@ -1110,6 +1164,20 @@ ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
   return *this;
 }
 
+ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
+  const std::shared_ptr<ModelAPI_AttributeDoubleArray>& theArray)
+{
+  *myDumpStorage << "[";
+  int aSize = theArray->size();
+  if (aSize > 0) {
+    *myDumpStorage << theArray->value(0);
+    for (int anIndex = 1; anIndex < aSize; ++anIndex)
+      *myDumpStorage << ", " << theArray->value(anIndex);
+  }
+  *myDumpStorage << "]";
+  return *this;
+}
+
 ModelHighAPI_Dumper& ModelHighAPI_Dumper::operator<<(
     const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr)
 {
index 211ce075d9c72313e3e450abe444be3702be7580..ef81ae2128e2466bce9df2205cb8b6f1fa408930 100644 (file)
@@ -36,10 +36,13 @@ class GeomAPI_Dir;
 class GeomDataAPI_Dir;
 class GeomDataAPI_Point;
 class GeomDataAPI_Point2D;
+class GeomDataAPI_Point2DArray;
 
 class ModelAPI_Attribute;
 class ModelAPI_AttributeBoolean;
 class ModelAPI_AttributeDouble;
+class ModelAPI_AttributeDoubleArray;
+class ModelAPI_AttributeIntArray;
 class ModelAPI_AttributeInteger;
 class ModelAPI_AttributeRefAttr;
 class ModelAPI_AttributeRefAttrList;
@@ -261,6 +264,9 @@ public:
   /// "X, Y"
   MODELHIGHAPI_EXPORT
   ModelHighAPI_Dumper& operator<<(const std::shared_ptr<GeomDataAPI_Point2D>& thePoint);
+  /// Dump GeomDataAPI_Point2DArray as a list of 2D points
+  MODELHIGHAPI_EXPORT
+  ModelHighAPI_Dumper& operator<<(const std::shared_ptr<GeomDataAPI_Point2DArray>& thePointArray);
 
   /// Dump AttributeBoolean
   MODELHIGHAPI_EXPORT
@@ -268,9 +274,15 @@ public:
   /// Dump AttributeInteger
   MODELHIGHAPI_EXPORT
   ModelHighAPI_Dumper& operator<<(const std::shared_ptr<ModelAPI_AttributeInteger>& theAttrInt);
+  /// Dump AttributeIntArray
+  MODELHIGHAPI_EXPORT
+  ModelHighAPI_Dumper& operator<<(const std::shared_ptr<ModelAPI_AttributeIntArray>& theArray);
   /// Dump AttributeDouble
   MODELHIGHAPI_EXPORT
   ModelHighAPI_Dumper& operator<<(const std::shared_ptr<ModelAPI_AttributeDouble>& theAttrReal);
+  /// Dump AttributeDoubleArray
+  MODELHIGHAPI_EXPORT
+  ModelHighAPI_Dumper& operator<<(const std::shared_ptr<ModelAPI_AttributeDoubleArray>& theArray);
   /// Dump AttributeString
   MODELHIGHAPI_EXPORT
   ModelHighAPI_Dumper& operator<<(const std::shared_ptr<ModelAPI_AttributeString>& theAttrStr);
index 2cf757443be12442821253fe138cfb1d0e9b11a4..509377003ce149c98d535a0ca4ae84d77741f61e 100644 (file)
@@ -148,7 +148,7 @@ static void dumpArray(std::ostringstream& theOutput, const double theArray[],
   for (int i = 0; i < theSize; ++i) {
     if (i > 0)
       theOutput << " ";
-    theOutput << std::fixed << setprecision(thePrecision)
+    theOutput << std::fixed << std::setprecision(thePrecision)
               << (fabs(theArray[i]) < TOLERANCE ? 0.0 : theArray[i]);
   }
 }
@@ -197,7 +197,7 @@ std::string ModelHighAPI_FeatureStore::dumpAttr(const AttributePtr& theAttr) {
   } else if (aType == ModelAPI_AttributeInteger::typeId()) {
     AttributeIntegerPtr anAttr = std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(theAttr);
     // do not dump a type of ConstraintAngle, because it can be changed due dumping
-    if (anAttr->id() == "AngleType") {
+    if (anAttr->id() == "AngleType" || anAttr->id() == "AngleTypePrevious") {
       return "";
     } else if (anAttr->id() == "LocationType") {
       return "__notinitialized__";
@@ -374,8 +374,10 @@ 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_Point2D::typeId()) {
-    // do not dump flyout point for constraints as it may be changed unexpectedly
-    if (theAttr->id() == "ConstraintFlyoutValuePnt")
+    // do not dump flyout point for constraints as it may be changed unexpectedly,
+    // also do not dump selection points controlled by GUI
+    if (theAttr->id() == "ConstraintFlyoutValuePnt" ||
+        theAttr->id() == "SelectedPointA" || theAttr->id() == "SelectedPointB")
       return "__notinitialized__";
     AttributePoint2DPtr anAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttr);
     double aValues[2] = {anAttr->x(), anAttr->y()};
@@ -407,9 +409,9 @@ std::string ModelHighAPI_FeatureStore::dumpShape(std::shared_ptr<GeomAPI_Shape>&
     aResult<<"Volume: ";
     // volumes of too huge shapes write in the scientific format
     if (aVolume >= 1.e5)
-      aResult<<std::scientific<<setprecision(7);
+      aResult<<std::scientific<<std::setprecision(7);
     else
-      aResult<<std::fixed<<setprecision(3);
+      aResult<<std::fixed<<std::setprecision(3);
     aResult<<aVolume<<std::endl;
   }
   std::shared_ptr<GeomAPI_Pnt> aCenter = GeomAlgoAPI_ShapeTools::centreOfMass(theShape);
index 03548d25270fe4ec2d63747e93fd1e8b68803bab..7d2edf482d8166faa14eb85bc84dc2599e4759ac 100644 (file)
@@ -27,7 +27,7 @@
 #include "ModelHighAPI_Interface.h"
 //--------------------------------------------------------------------------------------
 ModelHighAPI_RefAttr::ModelHighAPI_RefAttr()
-: myVariantType(VT_ATTRIBUTE)
+: myVariantType(VT_OBJECT)
 {
 }
 
index 8f1938720491f21997eee2676bc0b331c92a68e9..e2b0e3cbd5d5e86213f1d00eeddc6fdd8ef4e282 100644 (file)
 #include <GeomDataAPI_Dir.h>
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 //--------------------------------------------------------------------------------------
 #include <ModelAPI_AttributeBoolean.h>
 #include <ModelAPI_AttributeDocRef.h>
 #include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
 #include <ModelAPI_AttributeIntArray.h>
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeRefAttr.h>
@@ -239,6 +241,29 @@ void fillAttribute(const std::list<ModelHighAPI_Integer> & theValue,
     theAttribute->setValue(anIndex, it->intValue()); // use only values, no text support in array
 }
 
+//--------------------------------------------------------------------------------------
+void fillAttribute(const std::list<ModelHighAPI_Double> & theValue,
+                   const std::shared_ptr<ModelAPI_AttributeDoubleArray> & theAttribute)
+{
+  theAttribute->setSize(int(theValue.size()));
+
+  int anIndex = 0;
+  for (auto it = theValue.begin(); it != theValue.end(); ++it, ++anIndex)
+    theAttribute->setValue(anIndex, it->value()); // use only values, no text support in array
+}
+
+//--------------------------------------------------------------------------------------
+void fillAttribute(const std::list<std::shared_ptr<GeomAPI_Pnt2d> > & theValue,
+                   const std::shared_ptr<GeomDataAPI_Point2DArray> & theAttribute)
+{
+  theAttribute->setSize(int(theValue.size()));
+
+  int anIndex = 0;
+  for (auto it = theValue.begin(); it != theValue.end(); ++it, ++anIndex)
+    theAttribute->setPnt(anIndex, *it);
+}
+
+//--------------------------------------------------------------------------------------
 void fillAttribute(const ModelHighAPI_Double & theX,
                    const ModelHighAPI_Double & theY,
                    const ModelHighAPI_Double & theZ,
@@ -247,7 +272,6 @@ void fillAttribute(const ModelHighAPI_Double & theX,
   theX.fillAttribute(theAttribute, theX, theY, theZ);
 }
 
-
 //==================================================================================================
 GeomAPI_Shape::ShapeType shapeTypeByStr(std::string theShapeTypeStr)
 {
index d0fc586ca29eba28141ea0089ac37e382c44a8dd..e46596c3e1a0ea7191348fe68a073c08803d4bac 100644 (file)
@@ -37,9 +37,11 @@ class GeomAPI_Pnt2d;
 class GeomDataAPI_Dir;
 class GeomDataAPI_Point;
 class GeomDataAPI_Point2D;
+class GeomDataAPI_Point2DArray;
 //--------------------------------------------------------------------------------------
 class ModelAPI_AttributeBoolean;
 class ModelAPI_AttributeDouble;
+class ModelAPI_AttributeDoubleArray;
 class ModelAPI_AttributeIntArray;
 class ModelAPI_AttributeInteger;
 class ModelAPI_AttributeRefAttr;
@@ -146,6 +148,14 @@ MODELHIGHAPI_EXPORT
 void fillAttribute(const std::list<ModelHighAPI_Integer> & theValue,
                    const std::shared_ptr<ModelAPI_AttributeIntArray> & theAttribute);
 
+MODELHIGHAPI_EXPORT
+void fillAttribute(const std::list<ModelHighAPI_Double> & theValue,
+                   const std::shared_ptr<ModelAPI_AttributeDoubleArray> & theAttribute);
+
+MODELHIGHAPI_EXPORT
+void fillAttribute(const std::list<std::shared_ptr<GeomAPI_Pnt2d> > & theValue,
+                   const std::shared_ptr<GeomDataAPI_Point2DArray> & theAttribute);
+
 MODELHIGHAPI_EXPORT
 void fillAttribute(const ModelHighAPI_Double & theX,
                    const ModelHighAPI_Double & theY,
diff --git a/src/ModelHighAPI/Test/Test18451.py b/src/ModelHighAPI/Test/Test18451.py
new file mode 100644 (file)
index 0000000..14b1a2b
--- /dev/null
@@ -0,0 +1,500 @@
+# 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()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+model.addParameter(Part_1_doc, "ep", "0.5875")
+model.addParameter(Part_1_doc, "ep2", "0.35")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOZ"))
+SketchLine_1 = Sketch_1.addLine(-4.81482486096809e-34, 0.6, -0.08939999999999999, 0.6)
+SketchProjection_1 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_2 = SketchProjection_1.createdFeature()
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_2.result())
+SketchConstraintHorizontal_1 = Sketch_1.setHorizontal(SketchLine_1.result())
+SketchLine_3 = Sketch_1.addLine(-0.08939999999999999, 0.6, -0.08939999999999999, 0.46)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_3.startPoint())
+SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_3.result())
+SketchLine_4 = Sketch_1.addLine(-0.08939999999999999, 0.46, -0.2894, 0.46)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint())
+SketchConstraintHorizontal_2 = Sketch_1.setHorizontal(SketchLine_4.result())
+SketchLine_5 = Sketch_1.addLine(-0.2894, 0.46, -0.2894, 0.36)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint())
+SketchConstraintVertical_2 = Sketch_1.setVertical(SketchLine_5.result())
+SketchLine_6 = Sketch_1.addLine(-0.2894, 0.36, -0.08940000000000001, 0.36)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintHorizontal_3 = Sketch_1.setHorizontal(SketchLine_6.result())
+SketchLine_7 = Sketch_1.addLine(-0.08940000000000001, 0.36, -0.08940000000000001, -0.5600000000000001)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.startPoint())
+SketchConstraintVertical_3 = Sketch_1.setVertical(SketchLine_7.result())
+SketchLine_8 = Sketch_1.addLine(-0.08852125812784814, -0.571165474137811, -0.07285119438446976, -0.5729508523825384)
+SketchLine_9 = Sketch_1.addLine(-0.07285119438446976, -0.5729508523825384, -0.0654, -0.6028359607518391)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchLine_8.endPoint(), SketchLine_9.startPoint())
+SketchConstraintCoincidence_7.setName("SketchConstraintCoincidence_8")
+SketchLine_10 = Sketch_1.addLine(-0.0654, -0.6028359607518391, -0.0654, -1.285035960751839)
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchLine_9.endPoint(), SketchLine_10.startPoint())
+SketchConstraintCoincidence_8.setName("SketchConstraintCoincidence_9")
+SketchConstraintVertical_4 = Sketch_1.setVertical(SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(-0.0654, -1.285035960751839, 0.3076, -1.285035960751839)
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchLine_10.endPoint(), SketchLine_11.startPoint())
+SketchConstraintCoincidence_9.setName("SketchConstraintCoincidence_10")
+SketchConstraintHorizontal_4 = Sketch_1.setHorizontal(SketchLine_11.result())
+SketchLine_12 = Sketch_1.addLine(0.3076, -1.285035960751839, 0.3076, -1.045635960751839)
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchLine_11.endPoint(), SketchLine_12.startPoint())
+SketchConstraintCoincidence_10.setName("SketchConstraintCoincidence_11")
+SketchConstraintVertical_5 = Sketch_1.setVertical(SketchLine_12.result())
+SketchLine_13 = Sketch_1.addLine(0.3076, -1.045635960751839, 0.007599999999999971, -1.045635960751839)
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchLine_12.endPoint(), SketchLine_13.startPoint())
+SketchConstraintCoincidence_11.setName("SketchConstraintCoincidence_12")
+SketchConstraintHorizontal_5 = Sketch_1.setHorizontal(SketchLine_13.result())
+SketchLine_14 = Sketch_1.addLine(0.007599999999999971, -1.045635960751839, 0.007599999999999971, -0.3926359607518392)
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchLine_13.endPoint(), SketchLine_14.startPoint())
+SketchConstraintCoincidence_12.setName("SketchConstraintCoincidence_13")
+SketchConstraintVertical_6 = Sketch_1.setVertical(SketchLine_14.result())
+SketchLine_15 = Sketch_1.addLine(0.007599999999999971, -0.3926359607518392, 0, -0.3307389278992326)
+SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchLine_14.endPoint(), SketchLine_15.startPoint())
+SketchConstraintCoincidence_13.setName("SketchConstraintCoincidence_14")
+SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchLine_15.endPoint(), SketchLine_2.result())
+SketchConstraintCoincidence_14.setName("SketchConstraintCoincidence_21")
+SketchLine_16 = Sketch_1.addLine(-4.235164485203422e-23, 0.6, 0, -0.3307389278992326)
+SketchLine_16.setName("SketchLine_20")
+SketchLine_16.result().setName("SketchLine_20")
+SketchConstraintCoincidence_15 = Sketch_1.setCoincident(SketchLine_15.endPoint(), SketchLine_16.endPoint())
+SketchConstraintCoincidence_15.setName("SketchConstraintCoincidence_23")
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_1.result(), 0.08939999999999999)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_3.result(), 0.14)
+SketchConstraintLength_2.setName("SketchConstraintLength_5")
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_4.result(), 0.2)
+SketchConstraintLength_3.setName("SketchConstraintLength_6")
+SketchConstraintLength_4 = Sketch_1.setLength(SketchLine_5.result(), 0.1)
+SketchConstraintLength_4.setName("SketchConstraintLength_7")
+SketchConstraintCoincidence_16 = Sketch_1.setCoincident(SketchLine_7.startPoint(), SketchLine_3.result())
+SketchConstraintCoincidence_16.setName("SketchConstraintCoincidence_24")
+SketchConstraintLength_5 = Sketch_1.setLength(SketchLine_7.result(), 0.92)
+SketchConstraintLength_5.setName("SketchConstraintLength_8")
+SketchConstraintLength_6 = Sketch_1.setLength(SketchLine_10.result(), 0.6822)
+SketchConstraintLength_6.setName("SketchConstraintLength_9")
+SketchConstraintLength_7 = Sketch_1.setLength(SketchLine_11.result(), 0.373)
+SketchConstraintLength_7.setName("SketchConstraintLength_10")
+SketchConstraintLength_8 = Sketch_1.setLength(SketchLine_12.result(), 0.2394)
+SketchConstraintLength_8.setName("SketchConstraintLength_11")
+SketchConstraintLength_9 = Sketch_1.setLength(SketchLine_13.result(), 0.3)
+SketchConstraintLength_9.setName("SketchConstraintLength_12")
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_10.startPoint(), SketchLine_7.result(), 0.024, True)
+SketchPoint_1 = Sketch_1.addPoint(-0.08852125812784814, -0.571165474137811)
+SketchLine_17 = Sketch_1.addLine(-0.08940000000000001, -0.5600000000000001, -0.08852125812784814, -0.571165474137811)
+SketchLine_17.setName("SketchLine_21")
+SketchLine_17.result().setName("SketchLine_21")
+SketchConstraintCoincidence_17 = Sketch_1.setCoincident(SketchLine_7.endPoint(), SketchLine_17.startPoint())
+SketchConstraintCoincidence_17.setName("SketchConstraintCoincidence_7")
+SketchConstraintCoincidence_18 = Sketch_1.setCoincident(SketchLine_17.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_18.setName("SketchConstraintCoincidence_26")
+SketchConstraintCoincidence_19 = Sketch_1.setCoincident(SketchLine_8.startPoint(), SketchPoint_1.coordinates())
+SketchConstraintCoincidence_19.setName("SketchConstraintCoincidence_27")
+SketchConstraintCoincidence_20 = Sketch_1.setCoincident(SketchLine_17.endPoint(), SketchLine_8.startPoint())
+SketchConstraintCoincidence_20.setName("SketchConstraintCoincidence_28")
+SketchConstraintAngle_1 = Sketch_1.setAngleBackward(SketchLine_10.result(), SketchLine_9.result(), 166)
+SketchConstraintAngle_2 = Sketch_1.setAngleBackward(SketchLine_17.result(), SketchLine_8.result(), 101)
+SketchConstraintAngle_3 = Sketch_1.setAngleBackward(SketchLine_7.result(), SketchLine_17.result(), 175.5)
+SketchConstraintLength_10 = Sketch_1.setLength(SketchLine_17.result(), 0.0112)
+SketchConstraintLength_10.setName("SketchConstraintLength_13")
+SketchConstraintLength_11 = Sketch_1.setLength(SketchLine_9.result(), 0.0308)
+SketchConstraintLength_11.setName("SketchConstraintLength_14")
+SketchConstraintLength_12 = Sketch_1.setLength(SketchLine_14.result(), 0.653)
+SketchConstraintLength_12.setName("SketchConstraintLength_15")
+SketchConstraintAngle_4 = Sketch_1.setAngleBackward(SketchLine_14.result(), SketchLine_15.result(), 173)
+SketchProjection_2 = Sketch_1.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_18 = SketchProjection_2.createdFeature()
+SketchLine_18.setName("SketchLine_22")
+SketchLine_18.result().setName("SketchLine_22")
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_1.startPoint(), SketchLine_18.result(), 0.6, True)
+SketchLine_19 = Sketch_1.addLine(-0.08939999999999999, 0.46, -0.08940000000000001, 0.36)
+SketchLine_19.setName("SketchLine_23")
+SketchLine_19.result().setName("SketchLine_23")
+SketchConstraintCoincidence_21 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_19.startPoint())
+SketchConstraintCoincidence_21.setName("SketchConstraintCoincidence_29")
+SketchConstraintCoincidence_22 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_19.endPoint())
+SketchConstraintCoincidence_22.setName("SketchConstraintCoincidence_30")
+SketchConstraintCoincidence_23 = Sketch_1.setCoincident(SketchLine_16.startPoint(), SketchLine_1.startPoint())
+SketchConstraintCoincidence_23.setName("SketchConstraintCoincidence_25")
+model.do()
+Sketch_1.changeFacesOrder([[SketchLine_4.result(), SketchLine_5.result(), SketchLine_6.result(), SketchLine_19.result()],
+                           [SketchLine_1.result(), SketchLine_3.result(), SketchLine_19.result(), SketchLine_7.result(), SketchLine_17.result(), SketchLine_8.result(), SketchLine_9.result(), SketchLine_10.result(), SketchLine_11.result(), SketchLine_12.result(), SketchLine_13.result(), SketchLine_14.result(), SketchLine_15.result(), SketchLine_16.result()]
+                          ])
+Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_3f-SketchLine_23f-SketchLine_7f-SketchLine_21f-SketchLine_8f-SketchLine_9f-SketchLine_10f-SketchLine_11f-SketchLine_12f-SketchLine_13f-SketchLine_14f-SketchLine_15f-SketchLine_20r")], model.selection(), "ep", "ep")
+Extrusion_2 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_4f-SketchLine_5f-SketchLine_6f-SketchLine_23r")], model.selection(), "ep2", "ep2")
+Sketch_2 = model.addSketch(Part_1_doc, model.standardPlane("YOZ"))
+SketchLine_20 = Sketch_2.addLine(-3.021110559767089e-34, 0.6, -0.5875, 0.5401656539390112)
+SketchLine_20.setName("SketchLine_24")
+SketchLine_20.result().setName("SketchLine_24")
+SketchProjection_3 = Sketch_2.addProjection(model.selection("VERTEX", "Sketch_1/SketchLine_1_StartVertex"), False)
+SketchPoint_2 = SketchProjection_3.createdFeature()
+SketchConstraintCoincidence_24 = Sketch_2.setCoincident(SketchLine_20.startPoint(), SketchPoint_2.result())
+SketchConstraintCoincidence_24.setName("SketchConstraintCoincidence_31")
+SketchProjection_4 = Sketch_2.addProjection(model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_20][Extrusion_1_1/To_Face]"), False)
+SketchLine_21 = SketchProjection_4.createdFeature()
+SketchLine_21.setName("SketchLine_25")
+SketchLine_21.result().setName("SketchLine_25")
+SketchConstraintCoincidence_25 = Sketch_2.setCoincident(SketchLine_20.endPoint(), SketchLine_21.result())
+SketchConstraintCoincidence_25.setName("SketchConstraintCoincidence_32")
+SketchLine_22 = Sketch_2.addLine(-0.5875, 0.5401656539390112, -0.5875, 0.6)
+SketchLine_22.setName("SketchLine_26")
+SketchLine_22.result().setName("SketchLine_26")
+SketchConstraintCoincidence_26 = Sketch_2.setCoincident(SketchLine_20.endPoint(), SketchLine_22.startPoint())
+SketchConstraintCoincidence_26.setName("SketchConstraintCoincidence_33")
+SketchConstraintCoincidence_27 = Sketch_2.setCoincident(SketchAPI_Line(SketchLine_21).startPoint(), SketchLine_22.endPoint())
+SketchConstraintCoincidence_27.setName("SketchConstraintCoincidence_34")
+SketchProjection_5 = Sketch_2.addProjection(model.selection("EDGE", "[Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_1][Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_20]"), True)
+SketchLine_23 = SketchProjection_5.createdFeature()
+SketchLine_23.setName("SketchLine_27")
+SketchLine_23.result().setName("SketchLine_27")
+SketchProjection_6 = Sketch_2.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_24 = SketchProjection_6.createdFeature()
+SketchLine_24.setName("SketchLine_28")
+SketchLine_24.result().setName("SketchLine_28")
+SketchConstraintMirror_1 = Sketch_2.addMirror(SketchLine_24.result(), [SketchLine_20.result(), SketchLine_22.result()])
+[SketchLine_25, SketchLine_26] = SketchConstraintMirror_1.mirrored()
+SketchLine_26.setName("SketchLine_30")
+SketchLine_26.result().setName("SketchLine_30")
+SketchLine_25.setName("SketchLine_29")
+SketchLine_25.result().setName("SketchLine_29")
+model.do()
+ExtrusionCut_1 = model.addExtrusionCut(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_2")], model.selection(), model.selection("FACE", "Extrusion_1_1/Generated_Face&Sketch_1/SketchLine_7"), 0, model.selection(), 0, [model.selection("SOLID", "Extrusion_1_1")])
+Sketch_3 = model.addSketch(Part_1_doc, model.standardPlane("XOZ"))
+SketchLine_27 = Sketch_3.addLine(-2.319541823598068e-23, 0.1790140882246268, 0.375, 0.1396)
+SketchLine_27.setName("SketchLine_31")
+SketchLine_27.result().setName("SketchLine_31")
+SketchProjection_7 = Sketch_3.addProjection(model.selection("EDGE", "Sketch_1/SketchLine_20"), False)
+SketchLine_28 = SketchProjection_7.createdFeature()
+SketchLine_28.setName("SketchLine_32")
+SketchLine_28.result().setName("SketchLine_32")
+SketchConstraintCoincidence_28 = Sketch_3.setCoincident(SketchLine_27.startPoint(), SketchLine_28.result())
+SketchConstraintCoincidence_28.setName("SketchConstraintCoincidence_35")
+SketchLine_29 = Sketch_3.addLine(0.375, 0.1396, 3.347, 0.1396)
+SketchLine_29.setName("SketchLine_33")
+SketchLine_29.result().setName("SketchLine_33")
+SketchConstraintCoincidence_29 = Sketch_3.setCoincident(SketchLine_27.endPoint(), SketchLine_29.startPoint())
+SketchConstraintCoincidence_29.setName("SketchConstraintCoincidence_36")
+SketchConstraintHorizontal_6 = Sketch_3.setHorizontal(SketchLine_29.result())
+SketchLine_30 = Sketch_3.addLine(3.347, 0.1396, 3.347, 0)
+SketchLine_30.setName("SketchLine_34")
+SketchLine_30.result().setName("SketchLine_34")
+SketchConstraintCoincidence_30 = Sketch_3.setCoincident(SketchLine_29.endPoint(), SketchLine_30.startPoint())
+SketchConstraintCoincidence_30.setName("SketchConstraintCoincidence_37")
+SketchProjection_8 = Sketch_3.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_31 = SketchProjection_8.createdFeature()
+SketchLine_31.setName("SketchLine_35")
+SketchLine_31.result().setName("SketchLine_35")
+SketchConstraintCoincidence_31 = Sketch_3.setCoincident(SketchLine_30.endPoint(), SketchLine_31.result())
+SketchConstraintCoincidence_31.setName("SketchConstraintCoincidence_38")
+SketchConstraintVertical_7 = Sketch_3.setVertical(SketchLine_30.result())
+SketchLine_32 = Sketch_3.addLine(3.347, 0, 0, 0)
+SketchLine_32.setName("SketchLine_36")
+SketchLine_32.result().setName("SketchLine_36")
+SketchConstraintCoincidence_32 = Sketch_3.setCoincident(SketchLine_30.endPoint(), SketchLine_32.startPoint())
+SketchConstraintCoincidence_32.setName("SketchConstraintCoincidence_39")
+SketchConstraintCoincidence_33 = Sketch_3.setCoincident(SketchAPI_Line(SketchLine_31).startPoint(), SketchLine_32.endPoint())
+SketchConstraintCoincidence_33.setName("SketchConstraintCoincidence_40")
+SketchLine_33 = Sketch_3.addLine(-2.319541823598068e-23, 0.1790140882246268, 0, 0)
+SketchLine_33.setName("SketchLine_37")
+SketchLine_33.result().setName("SketchLine_37")
+SketchConstraintCoincidence_34 = Sketch_3.setCoincident(SketchLine_27.startPoint(), SketchLine_33.startPoint())
+SketchConstraintCoincidence_34.setName("SketchConstraintCoincidence_41")
+SketchConstraintCoincidence_35 = Sketch_3.setCoincident(SketchAPI_Line(SketchLine_31).startPoint(), SketchLine_33.endPoint())
+SketchConstraintCoincidence_35.setName("SketchConstraintCoincidence_42")
+SketchConstraintLength_13 = Sketch_3.setLength(SketchLine_32.result(), 3.347)
+SketchConstraintLength_13.setName("SketchConstraintLength_16")
+SketchProjection_9 = Sketch_3.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_34 = SketchProjection_9.createdFeature()
+SketchLine_34.setName("SketchLine_38")
+SketchLine_34.result().setName("SketchLine_38")
+SketchConstraintDistance_3 = Sketch_3.setDistance(SketchLine_27.endPoint(), SketchLine_34.result(), 0.375, True)
+SketchConstraintDistance_4 = Sketch_3.setDistance(SketchLine_27.endPoint(), SketchLine_32.result(), 0.1396, True)
+SketchConstraintAngle_5 = Sketch_3.setAngle(SketchLine_29.result(), SketchLine_27.result(), 174)
+model.do()
+Revolution_1 = model.addRevolution(Part_1_doc, [model.selection("COMPOUND", "Sketch_3")], model.selection("EDGE", "PartSet/OX"), 360, 0)
+Fuse_1_objects_1 = [model.selection("SOLID", "Extrusion_2_1"), model.selection("SOLID", "ExtrusionCut_1_1"), model.selection("SOLID", "Revolution_1_1")]
+Fuse_1 = model.addFuse(Part_1_doc, Fuse_1_objects_1, False)
+Fillet_1 = model.addFillet(Part_1_doc, [model.selection("EDGE", "[Fuse_1_1/Modified_Face&Sketch_1/SketchLine_20][Revolution_1_1/Generated_Face&Sketch_3/SketchLine_31]")], 0.05)
+Sketch_4 = model.addSketch(Part_1_doc, model.standardPlane("XOZ"))
+SketchLine_35 = Sketch_4.addLine(3.347, 0.183, 3.347, -0.344869487384476)
+SketchLine_35.setName("SketchLine_39")
+SketchLine_35.result().setName("SketchLine_39")
+SketchConstraintVertical_8 = Sketch_4.setVertical(SketchLine_35.result())
+SketchProjection_10 = Sketch_4.addProjection(model.selection("VERTEX", "[Revolution_1_1/Generated_Face&Sketch_3/SketchLine_33][Revolution_1_1/Generated_Face&Sketch_3/SketchLine_34]"), False)
+SketchPoint_3 = SketchProjection_10.createdFeature()
+SketchConstraintCoincidence_36 = Sketch_4.setCoincident(SketchAPI_Point(SketchPoint_3).coordinates(), SketchLine_35.result())
+SketchConstraintCoincidence_36.setName("SketchConstraintCoincidence_43")
+SketchLine_36 = Sketch_4.addLine(3.347, 0.183, 3.744000000000001, 0.183)
+SketchLine_36.setName("SketchLine_40")
+SketchLine_36.result().setName("SketchLine_40")
+SketchConstraintCoincidence_37 = Sketch_4.setCoincident(SketchLine_35.startPoint(), SketchLine_36.startPoint())
+SketchConstraintCoincidence_37.setName("SketchConstraintCoincidence_44")
+SketchConstraintHorizontal_7 = Sketch_4.setHorizontal(SketchLine_36.result())
+SketchLine_37 = Sketch_4.addLine(3.744000000000001, 0.183, 3.744000000000001, -0.192)
+SketchLine_37.setName("SketchLine_41")
+SketchLine_37.result().setName("SketchLine_41")
+SketchConstraintCoincidence_38 = Sketch_4.setCoincident(SketchLine_36.endPoint(), SketchLine_37.startPoint())
+SketchConstraintCoincidence_38.setName("SketchConstraintCoincidence_45")
+SketchConstraintVertical_9 = Sketch_4.setVertical(SketchLine_37.result())
+SketchConstraintLength_14 = Sketch_4.setLength(SketchLine_36.result(), 0.397)
+SketchConstraintLength_14.setName("SketchConstraintLength_17")
+SketchConstraintLength_15 = Sketch_4.setLength(SketchLine_37.result(), 0.375)
+SketchConstraintLength_15.setName("SketchConstraintLength_18")
+SketchArc_1 = Sketch_4.addArc(3.665500000000001, -0.192, 3.744000000000001, -0.192, 3.700263973859845, -0.2623826407679625, True)
+SketchConstraintCoincidence_39 = Sketch_4.setCoincident(SketchLine_37.endPoint(), SketchArc_1.startPoint())
+SketchConstraintCoincidence_39.setName("SketchConstraintCoincidence_46")
+SketchConstraintTangent_1 = Sketch_4.setTangent(SketchLine_37.result(), SketchArc_1.results()[1])
+SketchArc_2 = Sketch_4.addArc(3.347, 0.452830512615524, 3.700263973859845, -0.2623826407679625, 3.347, -0.344869487384476, True)
+SketchConstraintCoincidence_40 = Sketch_4.setCoincident(SketchArc_1.endPoint(), SketchArc_2.startPoint())
+SketchConstraintCoincidence_40.setName("SketchConstraintCoincidence_47")
+SketchConstraintTangent_2 = Sketch_4.setTangent(SketchArc_1.results()[1], SketchArc_2.results()[1])
+SketchConstraintCoincidence_41 = Sketch_4.setCoincident(SketchArc_2.endPoint(), SketchLine_35.endPoint())
+SketchConstraintCoincidence_41.setName("SketchConstraintCoincidence_48")
+SketchConstraintRadius_1 = Sketch_4.setRadius(SketchArc_1.results()[1], 0.0785)
+SketchProjection_11 = Sketch_4.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_38 = SketchProjection_11.createdFeature()
+SketchLine_38.setName("SketchLine_42")
+SketchLine_38.result().setName("SketchLine_42")
+SketchConstraintDistance_5 = Sketch_4.setDistance(SketchLine_35.startPoint(), SketchLine_38.result(), 0.183, True)
+SketchConstraintCoincidence_42 = Sketch_4.setCoincident(SketchArc_2.center(), SketchLine_35.result())
+SketchConstraintCoincidence_42.setName("SketchConstraintCoincidence_49")
+SketchConstraintRadius_2 = Sketch_4.setRadius(SketchArc_2.results()[1], 0.7977)
+model.do()
+Revolution_2 = model.addRevolution(Part_1_doc, [model.selection("FACE", "Sketch_4/Face-SketchLine_39r-SketchArc_2_2f-SketchArc_1_2f-SketchLine_41r-SketchLine_40r")], model.selection("EDGE", "Sketch_4/SketchLine_39"), 360, 0)
+Fuse_2 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Fillet_1_1"), model.selection("SOLID", "Revolution_2_1")], False)
+Sketch_5 = model.addSketch(Part_1_doc, model.selection("FACE", "Revolution_2_1/Generated_Face&Sketch_4/SketchLine_40"))
+SketchProjection_12 = Sketch_5.addProjection(model.selection("EDGE", "Sketch_4/SketchLine_40"), False)
+SketchLine_39 = SketchProjection_12.createdFeature()
+SketchLine_39.setName("SketchLine_43")
+SketchLine_39.result().setName("SketchLine_43")
+SketchCircle_1 = Sketch_5.addCircle(3.447, 0, 0.02725)
+SketchConstraintCoincidence_43 = Sketch_5.setCoincident(SketchLine_39.result(), SketchCircle_1.center())
+SketchConstraintCoincidence_43.setName("SketchConstraintCoincidence_50")
+SketchConstraintRadius_3 = Sketch_5.setRadius(SketchCircle_1.results()[1], 0.02725)
+SketchConstraintDistance_6 = Sketch_5.setDistance(SketchAPI_Line(SketchLine_39).startPoint(), SketchCircle_1.center(), 0.1, True)
+model.do()
+ExtrusionFuse_1 = model.addExtrusionFuse(Part_1_doc, [model.selection("COMPOUND", "Sketch_5")], model.selection(), 0.1123, 0, [model.selection("SOLID", "Fuse_2_1")])
+Sketch_6 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchProjection_13 = Sketch_6.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_40 = SketchProjection_13.createdFeature()
+SketchLine_40.setName("SketchLine_44")
+SketchLine_40.result().setName("SketchLine_44")
+SketchCircle_2 = Sketch_6.addCircle(3.347, 0, 0.0535464)
+SketchConstraintRadius_4 = Sketch_6.setRadius(SketchCircle_2.results()[1], 0.0535464)
+SketchIntersectionPoint_1 = Sketch_6.addIntersectionPoint(model.selection("EDGE", "Sketch_4/SketchLine_39"), False)
+[SketchPoint_4] = SketchIntersectionPoint_1.intersectionPoints()
+SketchConstraintCoincidence_44 = Sketch_6.setCoincident(SketchCircle_2.center(), SketchAPI_Point(SketchPoint_4).coordinates())
+SketchConstraintCoincidence_44.setName("SketchConstraintCoincidence_51")
+model.do()
+Extrusion_3 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_6/Face-SketchCircle_2_2f")], model.selection(), 0, 0.5586)
+Fuse_3 = model.addFuse(Part_1_doc, [model.selection("SOLID", "ExtrusionFuse_1_1"), model.selection("SOLID", "Extrusion_3_1")], False)
+Plane_4 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/YOZ"), 1.2912, False)
+Sketch_7 = model.addSketch(Part_1_doc, model.selection("FACE", "Plane_1"))
+SketchLine_41 = Sketch_7.addLine(-0.80851, 0.003178, -0.80851, 0.253178)
+SketchLine_41.setName("SketchLine_45")
+SketchLine_41.result().setName("SketchLine_45")
+SketchConstraintVertical_10 = Sketch_7.setVertical(SketchLine_41.result())
+SketchLine_42 = Sketch_7.addLine(-0.80851, 0.253178, -0.80851, 0.003178)
+SketchLine_42.setName("SketchLine_46")
+SketchLine_42.result().setName("SketchLine_46")
+SketchConstraintCoincidence_45 = Sketch_7.setCoincident(SketchLine_41.endPoint(), SketchLine_42.startPoint())
+SketchConstraintCoincidence_45.setName("SketchConstraintCoincidence_52")
+SketchConstraintCoincidence_46 = Sketch_7.setCoincident(SketchLine_41.startPoint(), SketchLine_42.endPoint())
+SketchConstraintCoincidence_46.setName("SketchConstraintCoincidence_53")
+SketchProjection_14 = Sketch_7.addProjection(model.selection("EDGE", "PartSet/OZ"), False)
+SketchLine_43 = SketchProjection_14.createdFeature()
+SketchLine_43.setName("SketchLine_47")
+SketchLine_43.result().setName("SketchLine_47")
+SketchProjection_15 = Sketch_7.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_44 = SketchProjection_15.createdFeature()
+SketchLine_44.setName("SketchLine_48")
+SketchLine_44.result().setName("SketchLine_48")
+SketchConstraintDistance_7 = Sketch_7.setDistance(SketchLine_41.startPoint(), SketchLine_43.result(), 0.80851, True)
+SketchConstraintDistance_7.setName("SketchConstraintDistance_8")
+SketchConstraintLength_16 = Sketch_7.setLength(SketchLine_41.result(), 0.25)
+SketchConstraintLength_16.setName("SketchConstraintLength_19")
+SketchArc_3 = Sketch_7.addArc(-0.7325, 0.253178, -0.80851, 0.253178, -0.7325, 0.3291879999999999, True)
+SketchConstraintCoincidence_47 = Sketch_7.setCoincident(SketchLine_41.endPoint(), SketchArc_3.startPoint())
+SketchConstraintCoincidence_47.setName("SketchConstraintCoincidence_54")
+SketchConstraintTangent_3 = Sketch_7.setTangent(SketchLine_41.result(), SketchArc_3.results()[1])
+SketchLine_45 = Sketch_7.addLine(-0.7325, 0.253178, -0.7325, 0.3291879999999999)
+SketchLine_45.setName("SketchLine_49")
+SketchLine_45.result().setName("SketchLine_49")
+SketchLine_45.setAuxiliary(True)
+SketchConstraintCoincidence_48 = Sketch_7.setCoincident(SketchArc_3.center(), SketchLine_45.startPoint())
+SketchConstraintCoincidence_48.setName("SketchConstraintCoincidence_55")
+SketchConstraintCoincidence_49 = Sketch_7.setCoincident(SketchArc_3.endPoint(), SketchLine_45.endPoint())
+SketchConstraintCoincidence_49.setName("SketchConstraintCoincidence_56")
+SketchConstraintVertical_11 = Sketch_7.setVertical(SketchLine_45.result())
+SketchLine_46 = Sketch_7.addLine(-0.7325, 0.3291879999999999, -0.6591320000000001, 0.3291879999999999)
+SketchLine_46.setName("SketchLine_50")
+SketchLine_46.result().setName("SketchLine_50")
+SketchConstraintCoincidence_50 = Sketch_7.setCoincident(SketchArc_3.endPoint(), SketchLine_46.startPoint())
+SketchConstraintCoincidence_50.setName("SketchConstraintCoincidence_57")
+SketchConstraintHorizontal_8 = Sketch_7.setHorizontal(SketchLine_46.result())
+SketchConstraintLength_17 = Sketch_7.setLength(SketchLine_46.result(), 0.073368)
+SketchConstraintLength_17.setName("SketchConstraintLength_20")
+SketchLine_47 = Sketch_7.addLine(-0.4815101245041479, 0.278, 0, -1.355252715606881e-20)
+SketchLine_47.setName("SketchLine_51")
+SketchLine_47.result().setName("SketchLine_51")
+SketchLine_48 = Sketch_7.addLine(0, -1.355252715606881e-20, -0.3375518398718391, -1.355252715606881e-20)
+SketchLine_48.setName("SketchLine_52")
+SketchLine_48.result().setName("SketchLine_52")
+SketchLine_48.setAuxiliary(True)
+SketchConstraintCoincidence_51 = Sketch_7.setCoincident(SketchLine_47.endPoint(), SketchLine_48.startPoint())
+SketchConstraintCoincidence_51.setName("SketchConstraintCoincidence_58")
+SketchConstraintHorizontal_9 = Sketch_7.setHorizontal(SketchLine_48.result())
+SketchConstraintAngle_6 = Sketch_7.setAngle(SketchLine_47.result(), SketchLine_48.result(), 30)
+SketchConstraintCoincidence_52 = Sketch_7.setCoincident(SketchLine_47.endPoint(), SketchAPI_Line(SketchLine_44).startPoint())
+SketchConstraintCoincidence_52.setName("SketchConstraintCoincidence_59")
+SketchConstraintDistance_8 = Sketch_7.setDistance(SketchLine_47.startPoint(), SketchLine_44.result(), 0.278, True)
+SketchConstraintDistance_8.setName("SketchConstraintDistance_9")
+SketchConstraintDistance_9 = Sketch_7.setDistance(SketchLine_45.startPoint(), SketchLine_43.result(), 0.7325, True)
+SketchConstraintDistance_9.setName("SketchConstraintDistance_10")
+SketchArc_4 = Sketch_7.addArc(-0.6591320000000001, -0.004579113372898896, -0.6591320000000001, 0.3291879999999999, -0.4815101245041478, 0.278, True)
+SketchConstraintCoincidence_53 = Sketch_7.setCoincident(SketchLine_46.endPoint(), SketchArc_4.startPoint())
+SketchConstraintCoincidence_53.setName("SketchConstraintCoincidence_60")
+SketchConstraintTangent_4 = Sketch_7.setTangent(SketchLine_46.result(), SketchArc_4.results()[1])
+SketchConstraintCoincidence_54 = Sketch_7.setCoincident(SketchArc_4.endPoint(), SketchLine_47.startPoint())
+SketchConstraintCoincidence_54.setName("SketchConstraintCoincidence_61")
+SketchConstraintDistance_10 = Sketch_7.setDistance(SketchLine_41.startPoint(), SketchLine_48.result(), 0.003178, True)
+SketchConstraintDistance_10.setName("SketchConstraintDistance_13")
+model.do()
+Plane_5 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OZ"), model.selection("VERTEX", "Sketch_7/SketchLine_45_StartVertex"), True)
+Sketch_8 = model.addSketch(Part_1_doc, model.selection("FACE", "Plane_2"))
+SketchProjection_16 = Sketch_8.addProjection(model.selection("VERTEX", "Sketch_7/SketchLine_45_StartVertex"), False)
+SketchPoint_5 = SketchProjection_16.createdFeature()
+SketchCircle_3 = Sketch_8.addCircle(1.2912, -0.80851, 0.02655)
+SketchConstraintCoincidence_55 = Sketch_8.setCoincident(SketchPoint_5.result(), SketchCircle_3.center())
+SketchConstraintCoincidence_55.setName("SketchConstraintCoincidence_62")
+SketchConstraintRadius_5 = Sketch_8.setRadius(SketchCircle_3.results()[1], 0.02655)
+model.do()
+Face_1 = model.addFace(Part_1_doc, [model.selection("FACE", "Sketch_8/Face-SketchCircle_3_2f")])
+Wire_1_objects = [model.selection("EDGE", "Sketch_7/SketchLine_45"), model.selection("EDGE", "Sketch_7/SketchArc_3_2"), model.selection("EDGE", "Sketch_7/SketchLine_50"), model.selection("EDGE", "Sketch_7/SketchArc_4_2"), model.selection("EDGE", "Sketch_7/SketchLine_51")]
+Wire_1 = model.addWire(Part_1_doc, Wire_1_objects)
+Pipe_1 = model.addPipe(Part_1_doc, [model.selection("FACE", "Face_1_1")], model.selection("WIRE", "Wire_1_1"))
+Fuse_4 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Pipe_1_1"), model.selection("SOLID", "Fuse_3_1")], False)
+Plane_6 = model.addPlane(Part_1_doc, model.selection("FACE", "PartSet/XOY"), 1.1407, True)
+Sketch_9 = model.addSketch(Part_1_doc, model.selection("FACE", "Plane_3"))
+SketchLine_49 = Sketch_9.addLine(0.5219, -0.3643, 0.5219, -0.1143)
+SketchLine_49.setName("SketchLine_53")
+SketchLine_49.result().setName("SketchLine_53")
+SketchConstraintVertical_12 = Sketch_9.setVertical(SketchLine_49.result())
+SketchProjection_17 = Sketch_9.addProjection(model.selection("EDGE", "PartSet/OY"), False)
+SketchLine_50 = SketchProjection_17.createdFeature()
+SketchLine_50.setName("SketchLine_54")
+SketchLine_50.result().setName("SketchLine_54")
+SketchProjection_18 = Sketch_9.addProjection(model.selection("EDGE", "PartSet/OX"), False)
+SketchLine_51 = SketchProjection_18.createdFeature()
+SketchLine_51.setName("SketchLine_55")
+SketchLine_51.result().setName("SketchLine_55")
+SketchConstraintDistance_11 = Sketch_9.setDistance(SketchLine_49.endPoint(), SketchLine_50.result(), 0.5219, True)
+SketchConstraintDistance_11.setName("SketchConstraintDistance_11")
+SketchConstraintDistance_12 = Sketch_9.setDistance(SketchLine_49.startPoint(), SketchLine_51.result(), 0.3643, True)
+SketchConstraintDistance_12.setName("SketchConstraintDistance_12")
+SketchConstraintLength_18 = Sketch_9.setLength(SketchLine_49.result(), 0.25)
+SketchConstraintLength_18.setName("SketchConstraintLength_21")
+SketchArc_5 = Sketch_9.addArc(0.4075999999999999, -0.1143, 0.5219, -0.1143, 0.4075999999999999, 2.344010062074957e-33, False)
+SketchConstraintCoincidence_56 = Sketch_9.setCoincident(SketchLine_49.endPoint(), SketchArc_5.startPoint())
+SketchConstraintCoincidence_56.setName("SketchConstraintCoincidence_63")
+SketchConstraintTangent_5 = Sketch_9.setTangent(SketchLine_49.result(), SketchArc_5.results()[1])
+SketchLine_52 = Sketch_9.addLine(0.4075999999999999, 2.344010062074957e-33, 0, -1.207680510878222e-22)
+SketchLine_52.setName("SketchLine_56")
+SketchLine_52.result().setName("SketchLine_56")
+SketchConstraintCoincidence_57 = Sketch_9.setCoincident(SketchArc_5.endPoint(), SketchLine_52.startPoint())
+SketchConstraintCoincidence_57.setName("SketchConstraintCoincidence_64")
+SketchConstraintHorizontal_10 = Sketch_9.setHorizontal(SketchLine_52.result())
+SketchConstraintCoincidence_58 = Sketch_9.setCoincident(SketchLine_52.endPoint(), SketchAPI_Line(SketchLine_50).startPoint())
+SketchConstraintCoincidence_58.setName("SketchConstraintCoincidence_65")
+SketchLine_53 = Sketch_9.addLine(0.4075999999999999, 2.344010062074957e-33, 0.4075999999999999, -0.1143)
+SketchLine_53.setName("SketchLine_57")
+SketchLine_53.result().setName("SketchLine_57")
+SketchLine_53.setAuxiliary(True)
+SketchConstraintCoincidence_59 = Sketch_9.setCoincident(SketchArc_5.endPoint(), SketchLine_53.startPoint())
+SketchConstraintCoincidence_59.setName("SketchConstraintCoincidence_66")
+SketchConstraintCoincidence_60 = Sketch_9.setCoincident(SketchArc_5.center(), SketchLine_53.endPoint())
+SketchConstraintCoincidence_60.setName("SketchConstraintCoincidence_67")
+SketchConstraintVertical_13 = Sketch_9.setVertical(SketchLine_53.result())
+model.do()
+Plane_7 = model.addPlane(Part_1_doc, model.selection("EDGE", "PartSet/OY"), model.selection("VERTEX", "Sketch_9/SketchLine_53_StartVertex"), True)
+Sketch_10 = model.addSketch(Part_1_doc, model.selection("FACE", "Plane_4"))
+SketchProjection_19 = Sketch_10.addProjection(model.selection("VERTEX", "Sketch_9/SketchLine_53_StartVertex"), False)
+SketchPoint_6 = SketchProjection_19.createdFeature()
+SketchCircle_4 = Sketch_10.addCircle(0.5219, 1.1407, 0.05355)
+SketchConstraintCoincidence_61 = Sketch_10.setCoincident(SketchPoint_6.result(), SketchCircle_4.center())
+SketchConstraintCoincidence_61.setName("SketchConstraintCoincidence_68")
+SketchConstraintRadius_6 = Sketch_10.setRadius(SketchCircle_4.results()[1], 0.05355)
+model.do()
+Face_2 = model.addFace(Part_1_doc, [model.selection("EDGE", "Sketch_10/SketchCircle_4_2")])
+Wire_2_objects = [model.selection("EDGE", "Sketch_9/SketchLine_53"), model.selection("EDGE", "Sketch_9/SketchArc_5_2"), model.selection("EDGE", "Sketch_9/SketchLine_56")]
+Wire_2 = model.addWire(Part_1_doc, Wire_2_objects)
+Pipe_2 = model.addPipe(Part_1_doc, [model.selection("FACE", "Face_2_1")], model.selection("WIRE", "Wire_2_1"))
+Fuse_5 = model.addFuse(Part_1_doc, [model.selection("SOLID", "Pipe_2_1"), model.selection("SOLID", "Fuse_4_1")], False)
+Symmetry_1 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Fuse_5_1")], model.selection("FACE", "PartSet/YOZ"), False)
+Symmetry_2 = model.addSymmetry(Part_1_doc, [model.selection("SOLID", "Symmetry_1_1")], model.selection("FACE", "PartSet/XOZ"), False)
+Group_1 = model.addGroup(Part_1_doc, "Faces", [model.selection("FACE", "Symmetry_2_1/MF:Symmetried&Pipe_2_1/From_Face_1")])
+Group_1.setName("Outlet_1")
+Group_1.result().setName("Outlet_1")
+Group_2 = model.addGroup(Part_1_doc, "Faces", [model.selection("FACE", "Symmetry_2_1/MF:Symmetried&Pipe_1_1/From_Face_1")])
+Group_2.setName("Inlet_1")
+Group_2.result().setName("Inlet_1")
+Group_3 = model.addGroup(Part_1_doc, "Faces", [model.selection("FACE", "Symmetry_2_1/MF:Symmetried&Extrusion_3_1/From_Face")])
+Group_3.setName("Outlet_2")
+Group_3.result().setName("Outlet_2")
+Group_4 = model.addGroup(Part_1_doc, "Faces", [model.selection("FACE", "Symmetry_2_1/MF:Symmetried&ExtrusionFuse_1_1/To_Face")])
+Group_4.setName("Inlet_2")
+Group_4.result().setName("Inlet_2")
+Group_5 = model.addGroup(Part_1_doc, "Faces", [model.selection("SOLID", "Symmetry_2_1")])
+Group_5.setName("Group_faces")
+Group_5.result().setName("Group_faces")
+GroupSubstraction_1_objects_2 = [model.selection("COMPOUND", "Outlet_1"), model.selection("COMPOUND", "Inlet_1"), model.selection("COMPOUND", "Outlet_2"), model.selection("COMPOUND", "Inlet_2")]
+GroupSubstraction_1 = model.addGroupSubstraction(Part_1_doc, [model.selection("COMPOUND", "Group_faces")], GroupSubstraction_1_objects_2)
+GroupSubstraction_1.result().setName("Wall")
+model.end()
+
+from GeomAPI import GeomAPI_Shape
+
+model.testNbResults(Symmetry_2, 1)
+model.testNbSubResults(Symmetry_2, [0])
+model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.SOLID, [1])
+model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.FACE, [44])
+model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.EDGE, [192])
+model.testNbSubShapes(Symmetry_2, GeomAPI_Shape.VERTEX, [384])
+model.testResultsVolumes(Symmetry_2, [0.714262940088946862715602037])
+
+from ModelAPI import *
+aFactory = ModelAPI_Session.get().validators()
+
+# Check that the features are not in error
+for i in range(Part_1_doc.size("Features")):
+  feature = objectToFeature(Part_1_doc.object("Features", i))
+  name = feature.name()
+  error = feature.error()
+  assert(error == ''), "The feature {0} is in error: {1}".format(name, error)
+  assert(aFactory.validate(feature)), "The feature {0} is in error: {1}".format(name, error)
+
+assert(Part_1_doc.size("Groups") == 6)
+
+assert(model.checkPythonDump())
index 1e5e8ac33d104300d2501f491ce88acc3f1303fc..706533ff23c87c1159879e60d545200a7a7f48d0 100644 (file)
@@ -29,7 +29,8 @@ ModuleBase_IWidgetCreator::~ModuleBase_IWidgetCreator()
 
 QWidget* ModuleBase_IWidgetCreator::createPanelByType(const std::string& theType,
                                                       QWidget* theParent,
-                                                      const FeaturePtr& theFeature)
+                                                      const FeaturePtr& theFeature,
+                                                      Config_WidgetAPI* theWidgetApi)
 {
   return 0;
 }
index 85219c27cb044e9132d2a9a6b1d75f9ac20796e0..8ffc9a0bfa135038f10ad277478d18e57387d7a8 100644 (file)
@@ -68,10 +68,12 @@ public:
   /// \param theType a panel type
   /// \param theParent a parent widget
   /// \param theFeature a feature modified in the panel
+  /// \param theWidgetApi a low-level API for reading xml definitions of widget
   /// \return created widget or null
   virtual QWidget* createPanelByType(const std::string& theType,
                                      QWidget* theParent,
-                                     const FeaturePtr& theFeature);
+                                     const FeaturePtr& theFeature,
+                                     Config_WidgetAPI* theWidgetApi = 0);
 
   /// Create page by its type
   /// The default implementation is empty
index de9582a5f86b32eb7c960bfb775d0ba447c2aa7c..3254241d2fa6028b063d48d68a34597d48ceaf34 100644 (file)
@@ -65,6 +65,7 @@ ModuleBase_ModelWidget::ModuleBase_ModelWidget(QWidget* theParent,
   myFeatureId = theData->featureId();
 
   myIsInternal = theData->getBooleanAttribute(ATTR_INTERNAL, false);
+  myUseExternalParts = theData->getBooleanAttribute("allow_parts_content", false);
 
   myIsModifiedInEdit = theData->getProperty(ATTR_MODIFIED_IN_EDIT);
 
index 4060181c0d412c27e0e812e9dc8393c2b1168ec4..f4d864d51192a7940bc344a825d361473fa562a1 100644 (file)
@@ -255,8 +255,8 @@ Q_OBJECT
   /// \param theFeature a feature object
   /// \param theToStoreValue a value about necessity to store the widget value to the feature
   /// \param isUpdateFlushed a flag if update should be flushed on store value
-  void setFeature(const FeaturePtr& theFeature, const bool theToStoreValue = false,
-                  const bool isUpdateFlushed = true);
+  virtual void setFeature(const FeaturePtr& theFeature, const bool theToStoreValue = false,
+                          const bool isUpdateFlushed = true);
 
   /// Editing mode depends on mode of current operation. This value is defined by it.
   virtual void setEditingMode(bool isEditing) { myIsEditing = isEditing; }
@@ -317,6 +317,9 @@ Q_OBJECT
 
   virtual bool isReadOnly() const { return !isEnabled(); }
 
+  /// Returns true if the widget should have access to external parts
+  bool canUseExternalParts() const { return myUseExternalParts; }
+
 signals:
   /// The signal about widget values are to be changed
   void beforeValuesChanged();
@@ -461,6 +464,9 @@ private:
   bool myFlushUpdateBlocked;
 
   bool myUpdateVisualAttributes;
+
+  /// A flag which indicates that current widget should have access to external parts
+  bool myUseExternalParts;
 };
 
 #endif
index 68e275d213504e78b2adf87a9fdbfe3bdc53328f..4d5f2a1c50d0f84b5912425809543a06540c30e5 100644 (file)
@@ -176,18 +176,26 @@ void ModuleBase_Preferences::createCustomPage(ModuleBase_IPrefMgr* thePref, int
       // Add item
       if (aProp->type() != Config_Prop::Disabled) {
         SUIT_PreferenceMgr::PrefItemType aPrefType = SUIT_PreferenceMgr::Auto;
-        if (aProp->type() == Config_Prop::Directory) {
+        switch (aProp->type()) {
+        case Config_Prop::Directory:
           aPrefType = SUIT_PreferenceMgr::File;
-        } else {
+          break;
+        case Config_Prop::Cursor:
+          aPrefType = SUIT_PreferenceMgr::Selector;
+          break;
+        default:
           aPrefType = (SUIT_PreferenceMgr::PrefItemType) aProp->type();
         }
+
         int anId = thePref->addPreference(QObject::tr(aProp->title().c_str()), aTab, aPrefType,
                                           QString::fromStdString(aProp->section()),
                                           QString::fromStdString(aProp->name()));
-        if(aProp->type() == Config_Prop::Directory) {
+
+        switch (aProp->type()) {
+        case Config_Prop::Directory:
           thePref->setItemProperty("path_type", Qtx::PT_Directory, anId);
-        }
-        if (aPrefType == SUIT_PreferenceMgr::DblSpin) {
+          break;
+        case SUIT_PreferenceMgr::DblSpin:
           if (aProp->min() != "") {
             double aMin = QString(aProp->min().c_str()).toDouble();
             thePref->setItemProperty("min", aMin, anId);
@@ -196,8 +204,8 @@ void ModuleBase_Preferences::createCustomPage(ModuleBase_IPrefMgr* thePref, int
             double aMax = QString(aProp->max().c_str()).toDouble();
             thePref->setItemProperty("max", aMax, anId);
           }
-        }
-        if (aPrefType == SUIT_PreferenceMgr::IntSpin) {
+          break;
+        case SUIT_PreferenceMgr::IntSpin:
           if (aProp->min() != "") {
             int aMin = QString(aProp->min().c_str()).toInt();
             thePref->setItemProperty("min", aMin, anId);
@@ -206,6 +214,20 @@ void ModuleBase_Preferences::createCustomPage(ModuleBase_IPrefMgr* thePref, int
             int aMax = QString(aProp->max().c_str()).toInt();
             thePref->setItemProperty("max", aMax, anId);
           }
+          break;
+        case Config_Prop::Cursor:
+          {
+            QList<QVariant> aIndicesList;
+            QList<QVariant> aIconsList;
+            aIndicesList << 0 << 1 << 2;
+            aIconsList << QPixmap(":pictures/ArrowCursor.png") <<
+              QPixmap(":pictures/CrossCursor.png") <<
+              QPixmap(":pictures/HandCursor.png");
+
+            thePref->setItemProperty("indexes", aIndicesList, anId);
+            thePref->setItemProperty("icons", aIconsList, anId);
+          }
+          break;
         }
       }
     }
index 9bf101d3ffffb9612aa9310509d56485afcf38c0..405dfb85a29df16395612eabc5589b1ceb23cca7 100644 (file)
@@ -18,6 +18,7 @@
 //
 
 #include "ModuleBase_ResultPrs.h"
+#include "ModuleBase_IViewer.h"
 
 #include <GeomAPI_PlanarEdges.h>
 
@@ -77,18 +78,35 @@ ModuleBase_ResultPrs::ModuleBase_ResultPrs(ResultPtr theResult)
   // Set own free boundaries aspect in order to have free
   // and unfree boundaries with different colors
   Handle(Prs3d_Drawer) aDrawer = Attributes();
-  Handle(Prs3d_LineAspect) aFreeBndAspect =
-    new Prs3d_LineAspect(Quantity_NOC_GREEN, Aspect_TOL_SOLID, 1);
-  aDrawer->SetFreeBoundaryAspect(aFreeBndAspect);
+  aDrawer->SetUnFreeBoundaryAspect(
+    new Prs3d_LineAspect(Quantity_NOC_YELLOW, Aspect_TOL_SOLID, 1));
+  aDrawer->SetFreeBoundaryAspect(new Prs3d_LineAspect(Quantity_NOC_GREEN, Aspect_TOL_SOLID, 1));
+  aDrawer->SetFaceBoundaryAspect(new Prs3d_LineAspect(Quantity_NOC_BLACK, Aspect_TOL_SOLID, 1));
+
+  aDrawer->VIsoAspect()->SetNumber(0);
+  aDrawer->UIsoAspect()->SetNumber(0);
 
   if (aDrawer->HasOwnPointAspect())
     aDrawer->PointAspect()->SetTypeOfMarker(Aspect_TOM_PLUS);
   else
     aDrawer->SetPointAspect(new Prs3d_PointAspect(Aspect_TOM_PLUS, Quantity_NOC_YELLOW, 1.));
 
+  aDrawer = DynamicHilightAttributes();
+  if (aDrawer.IsNull()) {
+    if (!ModuleBase_IViewer::DefaultHighlightDrawer.IsNull()) {
+      aDrawer = new Prs3d_Drawer(*ModuleBase_IViewer::DefaultHighlightDrawer);
+      aDrawer->VIsoAspect()->SetNumber(0);
+      aDrawer->UIsoAspect()->SetNumber(0);
+      SetDynamicHilightAttributes(aDrawer);
+    }
+  } else {
+    aDrawer->VIsoAspect()->SetNumber(0);
+    aDrawer->UIsoAspect()->SetNumber(0);
+  }
   myHiddenSubShapesDrawer = new AIS_ColoredDrawer(myDrawer);
   Handle(Prs3d_ShadingAspect) aShadingAspect = new Prs3d_ShadingAspect();
   aShadingAspect->SetMaterial(Graphic3d_NOM_BRASS); //default value of context material
+  aShadingAspect->Aspect()->SetEdgeColor(Quantity_NOC_BLACK);
   myHiddenSubShapesDrawer->SetShadingAspect(aShadingAspect);
 
   ModuleBase_Tools::setPointBallHighlighting(this);
@@ -117,28 +135,18 @@ void ModuleBase_ResultPrs::setEdgesDefaultColor()
     AttributeIntArrayPtr aColorAttr = myResult->data()->intArray(ModelAPI_Result::COLOR_ID());
     bool aHasColor = aColorAttr.get() && aColorAttr->isInitialized();
 
+    Handle(Prs3d_Drawer) aDrawer = Attributes();
+    aDrawer->SetFaceBoundaryDraw(Standard_True);
+    aDrawer->FaceBoundaryAspect()->SetColor(Quantity_NOC_BLACK);
+
     if (!aHasColor) {
-      Handle(Prs3d_Drawer) aDrawer = Attributes();
-      Handle(Prs3d_LineAspect) anAspect; // = aDrawer->LineAspect();
-      //anAspect->SetColor(Quantity_NOC_YELLOW);
-      //aDrawer->SetLineAspect(anAspect);
-
-      // - unfree boundaries color
-      anAspect = aDrawer->UnFreeBoundaryAspect();
-      anAspect->SetColor(Quantity_NOC_YELLOW);
-      aDrawer->SetUnFreeBoundaryAspect(anAspect);
-      aDrawer->SetUnFreeBoundaryDraw(true);
-
-      // - free boundaries color
-      anAspect = aDrawer->FreeBoundaryAspect();
-      anAspect->SetColor(Quantity_NOC_GREEN);
-      aDrawer->SetFreeBoundaryAspect(anAspect);
-      aDrawer->SetFreeBoundaryDraw(true);
-
-      // - standalone edges color
-      anAspect = aDrawer->WireAspect();
-      anAspect->SetColor(Quantity_NOC_RED);
-      aDrawer->SetWireAspect(anAspect);
+      aDrawer->UnFreeBoundaryAspect()->SetColor(Quantity_NOC_YELLOW);
+      aDrawer->FreeBoundaryAspect()->SetColor(Quantity_NOC_GREEN);
+      aDrawer->WireAspect()->SetColor(Quantity_NOC_RED);
+
+      aDrawer->SetUnFreeBoundaryDraw(Standard_True);
+      aDrawer->SetFreeBoundaryDraw(Standard_True);
+      aDrawer->SetWireDraw(Standard_True);
     }
   }
 }
index 70b885f451047bdd8fe1d487169c19824e0a2443..9a3c2f103160c9b8339cfe993353b39956703f89 100644 (file)
@@ -20,6 +20,9 @@
 #include "ModuleBase_ViewerFilters.h"
 #include "ModuleBase_IWorkshop.h"
 #include "ModuleBase_IModule.h"
+#include "ModuleBase_Operation.h"
+#include "ModuleBase_IPropertyPanel.h"
+#include "ModuleBase_ModelWidget.h"
 
 #include <ModelAPI_Session.h>
 #include <ModelAPI_Document.h>
@@ -76,7 +79,13 @@ Standard_Boolean ModuleBase_ShapeDocumentFilter::IsOk(
     if (aObj) {
       DocumentPtr aDoc = aObj->document();
       SessionPtr aMgr = ModelAPI_Session::get();
-      aValid = (aDoc == aMgr->activeDocument() || aDoc == aMgr->moduleDocument());
+
+      if (anOperation->propertyPanel()) {
+        ModuleBase_ModelWidget* aWidget = anOperation->propertyPanel()->activeWidget();
+        if (aWidget && aWidget->canUseExternalParts())
+          return Standard_True;
+      }
+      return (aDoc == aMgr->activeDocument() || aDoc == aMgr->moduleDocument());
     }
     else {
       // This object is not controlled by the filter
index 946a282a68eb28d825bc9542b1f58ca392f94fbf..b3122659346d5d5ca98be1d0400a1d07b67d51d9 100644 (file)
@@ -99,12 +99,13 @@ bool ModuleBase_WidgetCreatorFactory::hasPanelWidget(const std::string& theType)
 
 QWidget* ModuleBase_WidgetCreatorFactory::createPanelByType(const std::string& theType,
                                                             QWidget* theParent,
-                                                            const FeaturePtr& theFeature)
+                                                            const FeaturePtr& theFeature,
+                                                            Config_WidgetAPI* myWidgetApi)
 {
   QWidget* aPanel = 0;
   if (myPanelToCreator.contains(theType)) {
     WidgetCreatorPtr aCreator = myPanelToCreator[theType];
-    aPanel = aCreator->createPanelByType(theType, theParent, theFeature);
+    aPanel = aCreator->createPanelByType(theType, theParent, theFeature, myWidgetApi);
   }
   return aPanel;
 }
index 66c229a3b459466fb479f5391dc35f20bea06a4a..45cc5b5bb19fb5d6192c1b600400303fbdf987c0 100644 (file)
@@ -65,9 +65,12 @@ class MODULEBASE_EXPORT ModuleBase_WidgetCreatorFactory
   /// \param theType a type
   /// \param theParent a parent widget
   /// \param theFeature a feature to fill the panel
+  /// \param theWidgetApi the widget configuration.
+  ///                     The attribute of the model widget is obtained from XML
   /// \return a created panel or null
   QWidget* createPanelByType(const std::string& theType, QWidget* theParent,
-                             const FeaturePtr& theFeature);
+                             const FeaturePtr& theFeature,
+                             Config_WidgetAPI* theWidgetApi = 0);
 
   /// Returns true if there is a creator, which can make a page by the type
   /// \param theType a type
index 7f51a61a7cebe597e8b0cb4c1b41f1636c814238..ab7b17e103e4882e54c016ac02de634f101e2834 100644 (file)
@@ -161,8 +161,12 @@ void ModuleBase_WidgetFactory::createPanel(ModuleBase_PageBase* thePage,
   std::string aPanelName = myWidgetApi->getProperty(PROPERTY_PANEL_ID);
   if (!aPanelName.empty() && ModuleBase_WidgetCreatorFactory::get()->hasPanelWidget(aPanelName)) {
     QWidget* aPanel = ModuleBase_WidgetCreatorFactory::get()->createPanelByType(aPanelName,
-                                                               thePage->pageWidget(), theFeature);
-    thePage->addWidget(aPanel);
+        thePage->pageWidget(), theFeature, myWidgetApi);
+    ModuleBase_ModelWidget* aModelWdg = dynamic_cast<ModuleBase_ModelWidget*>(aPanel);
+    if (aModelWdg)
+      thePage->addModelWidget(aModelWdg);
+    else
+      thePage->addWidget(aPanel);
     thePage->alignToTop();
   }
 }
@@ -185,8 +189,8 @@ void ModuleBase_WidgetFactory::createWidget(ModuleBase_PageBase* thePage,
         aWidget->setVisible(false);
       }
     }
+    thePage->alignToTop();
   }
-  thePage->alignToTop();
 }
 
 void ModuleBase_WidgetFactory::getAttributeTitle(const std::string& theAttributeId,
@@ -200,6 +204,8 @@ void ModuleBase_WidgetFactory::getAttributeTitle(const std::string& theAttribute
       theTitle =
       QString::fromStdString(myWidgetApi->getProperty(CONTAINER_PAGE_NAME)).toStdString().c_str();
   }
+  else
+    theTitle = theAttributeId;
 }
 
 void ModuleBase_WidgetFactory::getGreedAttribute(std::string& theAttributeId)
index 33f14a17ec64014ab79ecf2543b408320d7690da..fae06823576e8d7bac17d965705f6761dfea4a49 100644 (file)
@@ -250,7 +250,7 @@ void ModuleBase_WidgetMultiSelector::deactivate()
 
   ModuleBase_WidgetSelector::deactivate();
   if (myVisibleObjects.size())
-    onShowOnly(false);
+    myShowOnlyBtn->setChecked(false);
 
   myWorkshop->module()->deactivateCustomPrs(ModuleBase_IModule::CustomizeHighlightedObjects, true);
   clearSelectedHistory();
index ca7c3c055209afb2a4ecff874246bb7de8230777..ada7b4eb06e56d5a0c5ff4cb57a13e074e8e90a3 100644 (file)
@@ -151,6 +151,7 @@ bool ModuleBase_WidgetPointInput::storeValueCustom()
         }
         aAttr->setText(aXText, aYText, aZText);
       } else {
+        aAttr->setText("", "", "");
         aAttr->setValue(myXSpin->value(), myYSpin->value(), myZSpin->value());
       }
     } else {
@@ -196,6 +197,7 @@ bool ModuleBase_WidgetPointInput::restoreValueCustom()
       myYSpin->setValue(myDefaultValue[1]);
       myZSpin->setValue(myDefaultValue[2]);
     }
+    setValueState(Stored);
     return true;
   }
   return false;
index b04a99508c6857c2faaf23c30ed45fbc52620924..5feef82281d02b4edef3f390da0ab191c00fcf00 100644 (file)
         <translation>La sélection est vide</translation>
     </message>
 </context>
+<context>
+    <name>ModuleBase_WidgetUndoLabel</name>
+    <message>
+        <source>Undo</source>
+        <translation>Undo</translation>
+    </message>
+</context>
 <context>
     <name>QObject</name>
     <message>
     <message>
         <source>The following parts will be deleted: %1.
 </source>
-        <translation>Les pièces suivantes seront supprimées : %1.
-
-</translation>
+        <translation>Les pièces suivantes seront supprimées : %1.</translation>
     </message>
     <message>
         <source>Replace</source>
index 83789b24f3b56cca7f72f5401121995d3fa231ec..9f87f6c3f8e40628488edfaea216871c781fe977 100644 (file)
@@ -59,8 +59,10 @@ SET(PROJECT_HEADERS
     PartSet_WidgetSketchLabel.h
     PartSet_CenterPrs.h
     PartSet_ExternalPointsMgr.h
-       PartSet_TreeNodes.h
-       PartSet_FieldStepPrs.h
+    PartSet_TreeNodes.h
+    PartSet_FieldStepPrs.h
+    PartSet_WidgetBSplinePoints.h
+    PartSet_BSplineWidget.h
 )
 
 SET(PROJECT_MOC_HEADERS
@@ -78,7 +80,9 @@ SET(PROJECT_MOC_HEADERS
     PartSet_WidgetShapeSelector.h
     PartSet_WidgetSketchCreator.h
     PartSet_WidgetSketchLabel.h
+    PartSet_WidgetBSplinePoints.h
     PartSet_ExternalPointsMgr.h
+    PartSet_BSplineWidget.h
 )
 
 SET(PROJECT_SOURCES
@@ -109,8 +113,10 @@ SET(PROJECT_SOURCES
     PartSet_WidgetSketchLabel.cpp
     PartSet_CenterPrs.cpp
     PartSet_ExternalPointsMgr.cpp
-       PartSet_TreeNodes.cpp
-       PartSet_FieldStepPrs.cpp
+    PartSet_TreeNodes.cpp
+    PartSet_FieldStepPrs.cpp
+    PartSet_WidgetBSplinePoints.cpp
+    PartSet_BSplineWidget.cpp
 )
 
 SET(PROJECT_RESOURCES
diff --git a/src/PartSet/PartSet_BSplineWidget.cpp b/src/PartSet/PartSet_BSplineWidget.cpp
new file mode 100644 (file)
index 0000000..9ca9744
--- /dev/null
@@ -0,0 +1,217 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <PartSet_BSplineWidget.h>
+
+#include <SketchPlugin_BSpline.h>
+
+#include <ModuleBase_Tools.h>
+
+#include <ModelAPI_AttributeDoubleArray.h>
+
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <GeomAPI_Pnt2d.h>
+
+#include <QFormLayout>
+#include <QGroupBox>
+#include <QLabel>
+#include <QVBoxLayout>
+#include <QScrollArea>
+#include <QToolButton>
+
+
+PartSet_BSplineWidget::PartSet_BSplineWidget(
+    QWidget* theParent,
+    const Config_WidgetAPI* theData)
+  : ModuleBase_ModelWidget(theParent, theData)
+{
+  QVBoxLayout* aMainLayout = new QVBoxLayout(this);
+  aMainLayout->setContentsMargins(0, 0, 0, 0);
+
+  // GroupBox to keep widgets for B-spline poles and weights
+  myPolesGroupBox = new QGroupBox(tr("Poles and weights"), this);
+  aMainLayout->addWidget(myPolesGroupBox);
+
+  QVBoxLayout* aLayout = new QVBoxLayout(myPolesGroupBox);
+  ModuleBase_Tools::adjustMargins(aLayout);
+
+  myScrollArea = new QScrollArea(myPolesGroupBox);
+  myScrollArea->setWidgetResizable(true);
+  myScrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  myScrollArea->setFrameStyle(QFrame::NoFrame);
+  aLayout->addWidget(myScrollArea);
+
+  QWidget* aContainer = new QWidget(myScrollArea);
+  QVBoxLayout* aBoxLay = new QVBoxLayout(aContainer);
+  aBoxLay->setContentsMargins(0, 0, 0, 0);
+
+  // layout of GroupBox
+  myPolesWgt = new QWidget(aContainer);
+  QGridLayout* aGroupLayout = new QGridLayout(myPolesWgt);
+  aGroupLayout->setColumnStretch(1, 1);
+  ModuleBase_Tools::adjustMargins(aGroupLayout);
+
+  restoreValueCustom();
+  aBoxLay->addWidget(myPolesWgt);
+  aBoxLay->addStretch(1);
+  myScrollArea->setWidget(aContainer);
+}
+
+void PartSet_BSplineWidget::setFeature(const FeaturePtr& theFeature,
+                                            const bool theToStoreValue,
+                                            const bool isUpdateFlushed)
+{
+  ModuleBase_ModelWidget::setFeature(theFeature, theToStoreValue, isUpdateFlushed);
+  restoreValueCustom();
+}
+
+void PartSet_BSplineWidget::deactivate()
+{
+  ModuleBase_ModelWidget::deactivate();
+  storeValueCustom();
+}
+
+
+QList<QWidget*> PartSet_BSplineWidget::getControls() const
+{
+  QList<QWidget*> aControls;
+  aControls.append(myScrollArea);
+  return aControls;
+}
+
+void PartSet_BSplineWidget::storePolesAndWeights() const
+{
+  std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
+  AttributeDoubleArrayPtr aWeightsArray = aData->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
+
+  std::list<BSplinePoleWidgets>::const_iterator anIt = myPoles.begin();
+  for (int anIndex = 0; anIt != myPoles.end(); ++anIndex, ++anIt) {
+    aWeightsArray->setValue(anIndex, anIt->myWeight->value());
+  }
+}
+
+bool PartSet_BSplineWidget::storeValueCustom()
+{
+  std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
+  if (!aData || !aData->isValid()) // can be on abort of sketcher element
+    return false;
+
+  AttributeDoubleArrayPtr aWeights = aData->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
+
+  bool isBlocked = blockSignals(true);
+  storePolesAndWeights();
+  ModuleBase_Tools::flushUpdated(myFeature);
+  blockSignals(isBlocked);
+
+  updateObject(myFeature);
+  return true;
+}
+
+bool PartSet_BSplineWidget::restoreValueCustom()
+{
+  if (!myFeature)
+    return false;
+
+  DataPtr aData = myFeature->data();
+
+  AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+      aData->attribute(SketchPlugin_BSpline::POLES_ID()));
+  AttributeDoubleArrayPtr aWeights = aData->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
+
+  while (myPoles.size() < aPoles->size())
+    addPoleWidget();
+
+  std::list<BSplinePoleWidgets>::iterator anIt = myPoles.begin();
+  for (int anIndex = 0; anIt != myPoles.end(); ++anIt, ++anIndex) {
+    GeomPnt2dPtr aPoint = aPoles->pnt(anIndex);
+    anIt->myX->setValue(aPoint->x());
+    anIt->myY->setValue(aPoint->y());
+    bool isBlocked = anIt->myWeight->blockSignals(true);
+    anIt->myWeight->setValue(aWeights->value(anIndex));
+    anIt->myWeight->blockSignals(isBlocked);
+  }
+
+  return true;
+}
+
+void PartSet_BSplineWidget::addPoleWidget()
+{
+  QGridLayout* aGroupLay = dynamic_cast<QGridLayout*>(myPolesWgt->layout());
+  int aNbPoles = (int)myPoles.size();
+  QString aPoleStr = tr("Pole %1").arg(aNbPoles + 1);
+
+  myPoles.push_back(BSplinePoleWidgets());
+  BSplinePoleWidgets& aPole = myPoles.back();
+  aGroupLay->addWidget(createPoleWidget(aPole, aPoleStr, myPolesWgt), aNbPoles, 1);
+}
+
+QGroupBox* PartSet_BSplineWidget::createPoleWidget(BSplinePoleWidgets& thePole,
+  const QString& theName, QWidget* theParent)
+{
+  QGroupBox* aPoleGroupBox = new QGroupBox(theName, theParent);
+  QGridLayout* aPoleLay = new QGridLayout(aPoleGroupBox);
+  aPoleLay->setSpacing(0);
+  ModuleBase_Tools::zeroMargins(aPoleLay);
+
+  thePole.myX = new ModuleBase_LabelValue(aPoleGroupBox, tr("X"));
+  aPoleLay->addWidget(thePole.myX, 0, 0, 1, 3);
+  thePole.myY = new ModuleBase_LabelValue(aPoleGroupBox, tr("Y"));
+  aPoleLay->addWidget(thePole.myY, 1, 0, 1, 3);
+  thePole.myWeight = new ModuleBase_ParamSpinBox(aPoleGroupBox);
+  thePole.myWeight->setMinimum(0.0);
+
+  aPoleLay->addWidget(new QLabel(tr("Weight :"), aPoleGroupBox), 2, 0);
+  aPoleLay->addWidget(thePole.myWeight, 2, 1);
+  // we should listen textChanged signal as valueChanged do not send when text is modified
+  connect(thePole.myWeight, SIGNAL(textChanged(const QString&)),
+    this, SIGNAL(valuesChanged()));
+
+  thePole.myAddBtn = new QToolButton(aPoleGroupBox);
+  thePole.myAddBtn->setIcon(QIcon(":pictures/add.png"));
+  thePole.myAddBtn->setToolTip(tr("Add a new pole after the current"));
+  aPoleLay->addWidget(thePole.myAddBtn, 2, 2);
+  connect(thePole.myAddBtn, SIGNAL(clicked(bool)), SLOT(onAddPole()));
+
+  return aPoleGroupBox;
+}
+
+
+void PartSet_BSplineWidget::onAddPole()
+{
+  QObject* aObj = sender();
+  std::list<BSplinePoleWidgets>::const_iterator aIt;
+  int aId = 0;
+  bool aFound = false;
+  for (aIt = myPoles.cbegin(); aIt != myPoles.cend(); aIt++, aId++) {
+    if ((*aIt).myAddBtn == aObj) {
+      aFound = true;
+      break;
+    }
+  }
+  if (aFound) {
+    // add a new pole after found Id
+    std::ostringstream anActionName;
+    anActionName << SketchPlugin_BSplineBase::ADD_POLE_ACTION_ID() << "#" << aId;
+    if (feature()->customAction(anActionName.str()))
+      updateObject(feature());
+
+    restoreValueCustom();
+  }
+}
diff --git a/src/PartSet/PartSet_BSplineWidget.h b/src/PartSet/PartSet_BSplineWidget.h
new file mode 100644 (file)
index 0000000..05108ca
--- /dev/null
@@ -0,0 +1,100 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef PartSet_BSplineWidget_H
+#define PartSet_BSplineWidget_H
+
+
+#include <PartSet.h>
+
+#include <ModelAPI_Feature.h>
+
+#include <ModuleBase_LabelValue.h>
+#include <ModuleBase_ModelWidget.h>
+#include <ModuleBase_ParamSpinBox.h>
+
+class QGroupBox;
+class QScrollArea;
+class QToolButton;
+
+/** \brief Represent a content of the property panel to show/modify parameters of B-spline curve.
+ *  \ingroup GUI
+ */
+class PartSet_BSplineWidget : public ModuleBase_ModelWidget
+{
+Q_OBJECT
+public:
+  /// Constructor
+  /// \param theParent the parent object
+  /// \param theData the widget configuation. The attribute of the model widget is obtained from
+  PartSet_BSplineWidget(QWidget* theParent,
+                             const Config_WidgetAPI* theData);
+
+  virtual ~PartSet_BSplineWidget() {}
+
+  /// The methiod called when widget is deactivated
+  virtual void deactivate();
+
+  /// Returns list of widget controls
+  /// \return a control list
+  virtual QList<QWidget*> getControls() const;
+
+  /// Set feature which is processing by active operation
+  /// \param theFeature a feature object
+  /// \param theToStoreValue a value about necessity to store the widget value to the feature
+  /// \param isUpdateFlushed a flag if update should be flushed on store value
+  virtual void setFeature(const FeaturePtr& theFeature, const bool theToStoreValue = false,
+                          const bool isUpdateFlushed = true);
+
+protected:
+  /// Saves the internal parameters to the given feature
+  /// \return True in success
+  virtual bool storeValueCustom();
+
+  /// Restore value from attribute data to the widget's control
+  virtual bool restoreValueCustom();
+
+  /// Create group of widgets related to coordinates of pole and its weight
+  void addPoleWidget();
+
+  /// Update attributes of B-spline feature
+  void storePolesAndWeights() const;
+
+private slots:
+  void onAddPole();
+
+private:
+  struct BSplinePoleWidgets {
+    ModuleBase_LabelValue* myX;
+    ModuleBase_LabelValue* myY;
+    ModuleBase_ParamSpinBox* myWeight;
+    QToolButton* myAddBtn;
+  };
+
+  QGroupBox* createPoleWidget(BSplinePoleWidgets& thePole,
+                              const QString& theName,
+                              QWidget* theParent);
+
+  QWidget* myPolesWgt; ///< widget to show poles and weights of B-spline curve
+  QGroupBox* myPolesGroupBox;
+  QScrollArea* myScrollArea;
+  std::list<BSplinePoleWidgets> myPoles; ///< list of B-spline poles and their weights
+};
+
+#endif
\ No newline at end of file
index 0ea7aa78d4efeafc27614cefd638c72bb8f6cb80..7ec0f7e819fc4e75dc90facf860ce915469fddef 100644 (file)
@@ -37,6 +37,7 @@
 #include <ModuleBase_Operation.h>
 #include <ModuleBase_OperationFeature.h>
 #include <ModuleBase_ViewerPrs.h>
+#include <ModuleBase_IViewer.h>
 #include <ModuleBase_Tools.h>
 
 #include <XGUI_ModuleConnector.h>
@@ -45,6 +46,7 @@
 #include <XGUI_DataModel.h>
 #include <XGUI_OperationMgr.h>
 #include <XGUI_ObjectsBrowser.h>
+#include <XGUI_ViewerProxy.h>
 
 #include <Events_Loop.h>
 #include <ModelAPI_Events.h>
@@ -55,6 +57,7 @@
 #include <QAction>
 #include <QMenu>
 #include <QEvent>
+#include <QApplication>
 
 #include <TopoDS.hxx>
 #include <BRep_Tool.hxx>
@@ -103,6 +106,10 @@ void PartSet_MenuMgr::createActions()
   aAction = ModuleBase_Tools::createAction(QIcon(":icons/edit.png"), tr("Edit..."), aParent,
                          this, SLOT(onEdit(bool)));
   myActions["EDIT_CMD"] = aAction;
+
+  aAction = ModuleBase_Tools::createAction(QIcon(":icons/activate.png"), tr("Load all parts"),
+    aParent, this, SLOT(onActivateAllParts()));
+  myActions["ACTIVATE_ALL_PARTS_CMD"] = aAction;
 }
 
 
@@ -503,6 +510,39 @@ void PartSet_MenuMgr::activatePart(ResultPartPtr thePart) const
   }
 }
 
+void PartSet_MenuMgr::onActivateAllParts()
+{
+  SessionPtr aMgr = ModelAPI_Session::get();
+  if (aMgr->isOperation())
+    return;
+
+  DocumentPtr aDoc = aMgr->moduleDocument();
+  int aNbParts = aDoc->size(ModelAPI_ResultPart::group());
+  bool isActivated = false;
+  QList<ResultPartPtr> aPartsToLoad;
+  for (int i = 0; i < aNbParts; i++) {
+    ObjectPtr aObj = aDoc->object(ModelAPI_ResultPart::group(), i);
+    ResultPartPtr aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
+    if (!aPartRes->partDoc().get())
+      aPartsToLoad.append(aPartRes);
+  }
+  if (!aPartsToLoad.isEmpty()) {
+    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+    aMgr->startOperation("All Parts loading");
+    foreach(ResultPartPtr aPartRes, aPartsToLoad) {
+      aPartRes->loadPart();
+    }
+    aMgr->finishOperation();
+
+    XGUI_Workshop* aWorkshop = myModule->getWorkshop();
+    XGUI_ObjectsBrowser* aObjBrowser = aWorkshop->objectBrowser();
+    aObjBrowser->update();
+    aWorkshop->viewer()->update();
+    aWorkshop->updateCommandStatus();
+    QApplication::restoreOverrideCursor();
+  }
+}
+
 void PartSet_MenuMgr::onActivatePartSet(bool)
 {
   if (myModule->workshop()->currentOperation())
@@ -522,6 +562,7 @@ void PartSet_MenuMgr::activatePartSet() const
     aMgr->finishOperation();
 
   myModule->workshop()->updateCommandStatus();
+  myModule->workshop()->viewer()->update();
 }
 
 void PartSet_MenuMgr::grantedOperationIds(ModuleBase_Operation* theOperation,
index 1b7bec76196e60ca2ae872f08bf3e8dd0ef143d3..2bea22d23c92e2bf04ae7d4d83a849dd3e5ddbcc 100644 (file)
@@ -100,6 +100,9 @@ private slots:
   /// A slot called on edit of feature
   void onEdit(bool);
 
+  /// Activates all not loaded parts
+  void onActivateAllParts();
+
 protected:
   /// Redefinition of virtual method
   /// \param theObj an object
index 5b50689714efd32cde8bd4584cae4d8411858834..1e8d2882fcf48db61638328c4d42444f4276c735 100644 (file)
@@ -22,6 +22,7 @@
 #include "PartSet_Validators.h"
 #include "PartSet_Tools.h"
 #include "PartSet_PreviewPlanes.h"
+#include "PartSet_WidgetBSplinePoints.h"
 #include "PartSet_WidgetPoint2d.h"
 #include "PartSet_WidgetPoint2DFlyout.h"
 #include "PartSet_WidgetShapeSelector.h"
@@ -39,6 +40,7 @@
 #include "PartSet_OverconstraintListener.h"
 #include "PartSet_TreeNodes.h"
 #include "PartSet_FieldStepPrs.h"
+#include "PartSet_BSplineWidget.h"
 
 #include "PartSet_Filters.h"
 #include "PartSet_FilterInfinite.h"
@@ -459,9 +461,9 @@ void PartSet_Module::updateSketcherOnStart(ModuleBase_Operation* 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);
-  //}
+  else if (sketchMgr()->isNestedSketchOperation(theOperation)) {
+    mySketchMgr->startNestedSketch(theOperation);
+  }
 }
 
 //******************************************************
@@ -804,12 +806,12 @@ bool PartSet_Module::createWidgets(const FeaturePtr& theFeature, const QString&
             return aProcessed;
         }
         const TopoDS_Shape& aTDShape = aShape->impl<TopoDS_Shape>();
-        AttributePtr anAttribute = PartSet_Tools::findAttributeBy2dPoint(anObject, aTDShape,
-                                                               mySketchMgr->activeSketch());
-        if (anAttribute.get()) {
+        std::pair<AttributePtr, int> anAttribute =
+            PartSet_Tools::findAttributeBy2dPoint(anObject, aTDShape, mySketchMgr->activeSketch());
+        if (anAttribute.first.get()) {
           ModuleBase_WidgetFactory aFactory(theXmlRepr.toStdString(), workshop());
 
-          const std::string anAttributeId = anAttribute->id();
+          const std::string anAttributeId = anAttribute.first->id();
           aFactory.createWidget(aPropertyPanel->contentWidget(), anAttributeId);
 
           theWidgets = aFactory.getModelWidgets();
@@ -915,16 +917,27 @@ ModuleBase_ModelWidget* PartSet_Module::createWidgetByType(const std::string& th
     aPointSelectorWgt->setSketcher(mySketchMgr->activeSketch());
     aWgt = aPointSelectorWgt;
   }
+  else if (theType == "sketch-bspline_selector") {
+    PartSet_WidgetBSplinePoints* aBSplineWgt =
+        new PartSet_WidgetBSplinePoints(theParent, aWorkshop, theWidgetApi);
+    aBSplineWgt->setSketch(mySketchMgr->activeSketch());
+    aWgt = aBSplineWgt;
+  }
   else if (theType == WDG_DOUBLEVALUE_EDITOR) {
     aWgt = new PartSet_WidgetEditor(theParent, aWorkshop, theWidgetApi);
   } else if (theType == "export_file_selector") {
     aWgt = new PartSet_WidgetFileSelector(theParent, aWorkshop, theWidgetApi);
   } else if (theType == "sketch_launcher") {
     aWgt = new PartSet_WidgetSketchCreator(theParent, this, theWidgetApi);
-  } else if (theType == "module_choice") {
+  }
+  else if (theType == "module_choice") {
     aWgt = new ModuleBase_WidgetChoice(theParent, theWidgetApi);
     connect(aWgt, SIGNAL(itemSelected(ModuleBase_ModelWidget*, int)),
-            this, SLOT(onChoiceChanged(ModuleBase_ModelWidget*, int)));
+      this, SLOT(onChoiceChanged(ModuleBase_ModelWidget*, int)));
+  } else if (theType == "bspline-panel") {
+    PartSet_BSplineWidget* aPanel = new PartSet_BSplineWidget(theParent, theWidgetApi);
+    //aPanel->setFeature(theFeature);
+    aWgt = aPanel;
   }
   return aWgt;
 }
@@ -1300,6 +1313,27 @@ void PartSet_Module::onActiveDocPopup(const QPoint& thePnt)
 
   QMenu aMenu;
   aMenu.addAction(aActivatePartAction);
+
+#ifndef HAVE_SALOME
+  if (aMgr->activeDocument() == aMgr->moduleDocument()) {
+    DocumentPtr aDoc = aMgr->moduleDocument();
+    int aNbParts = aDoc->size(ModelAPI_ResultPart::group());
+    bool aHaveToActivate = false;
+    for (int i = 0; i < aNbParts; i++) {
+      ObjectPtr aObj = aDoc->object(ModelAPI_ResultPart::group(), i);
+      ResultPartPtr aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
+      if (!aPartRes->partDoc().get()) {
+        aHaveToActivate = true;
+        break;
+      }
+    }
+    if (aHaveToActivate) {
+      QAction* aActivateAllPartAction = myMenuMgr->action("ACTIVATE_ALL_PARTS_CMD");
+      aMenu.addAction(aActivateAllPartAction);
+    }
+  }
+#endif
+
   aMenu.exec(aHeader->mapToGlobal(thePnt));
 }
 
@@ -1551,17 +1585,14 @@ void PartSet_Module::processEvent(const std::shared_ptr<Events_Message>& theMess
     XGUI_Displayer* aDisplayer = aWorkshop->displayer();
     QObjectPtrList aObjects = aDisplayer->displayedObjects();
     bool aHidden;
-    bool aUpdateViewer = false;
     foreach(ObjectPtr aObj, aObjects) {
       aHidden = !aObj->data() || !aObj->data()->isValid() ||
         aObj->isDisabled() || (!aObj->isDisplayed());
       if (!aHidden) {
         aDisplayer->redisplay(aObj, false);
-        aUpdateViewer = true;
       }
     }
-    if (aUpdateViewer)
-     aDisplayer->updateViewer();
+    aDisplayer->updateViewer();
     // Update tree items if they are expanded
     if (needUpdate) {
       aTreeView->viewport()->update(aTreeView->viewport()->rect());
@@ -1728,8 +1759,9 @@ AttributePtr PartSet_Module::findAttribute(const ObjectPtr& theObject,
 
   if (aGeomShape.get()) {
     TopoDS_Shape aTDSShape = aGeomShape->impl<TopoDS_Shape>();
-    return PartSet_Tools::findAttributeBy2dPoint(theObject, aTDSShape,
-                                                 mySketchMgr->activeSketch());
+    std::pair<AttributePtr, int> anAttrAndIndex =
+        PartSet_Tools::findAttributeBy2dPoint(theObject, aTDSShape, mySketchMgr->activeSketch());
+    return anAttrAndIndex.first;
   }
   return anAttribute;
 }
index e8257714569d0363873ca9a0db1b851984a35787..bcf6db4be9bedf1554dc90a14d4d49f0b548d4ee 100644 (file)
@@ -60,6 +60,7 @@
 #include <ModuleBase_ViewerFilters.h>
 
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 
 #include <GeomAPI_Shape.h>
 
 void getAttributesOrResults(const Handle(SelectMgr_EntityOwner)& theOwner,
                             const FeaturePtr& theFeature, const FeaturePtr& theSketch,
                             const ResultPtr& theResult,
-                            std::set<AttributePtr>& theSelectedAttributes,
+                            std::map<AttributePtr, int>& theSelectedAttributes,
                             std::set<ResultPtr>& theSelectedResults,
                             TopTools_MapOfShape& theShapes)
 {
@@ -156,10 +157,10 @@ void getAttributesOrResults(const Handle(SelectMgr_EntityOwner)& theOwner,
     theShapes.Add(aShape);
     TopAbs_ShapeEnum aShapeType = aShape.ShapeType();
     if (aShapeType == TopAbs_VERTEX) {
-      AttributePtr aPntAttr = PartSet_Tools::findAttributeBy2dPoint(theFeature,
-                                                                    aShape, theSketch);
-      if (aPntAttr.get() != NULL)
-        theSelectedAttributes.insert(aPntAttr);
+      std::pair<AttributePtr, int> aPntAttrIndex =
+          PartSet_Tools::findAttributeBy2dPoint(theFeature, aShape, theSketch);
+      if (aPntAttrIndex.first.get() != NULL)
+        theSelectedAttributes[aPntAttrIndex.first] = aPntAttrIndex.second;
     }
     else if (aShapeType == TopAbs_EDGE &&
              theSelectedResults.find(theResult) == theSelectedResults.end()) {
@@ -226,15 +227,15 @@ void PartSet_SketcherMgr::onEnterViewPort()
 
   // 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));
+    if (canChangeCursor(getCurrentOperation())) {
+      QCursor* aCurrentCursor = QApplication::overrideCursor();
+      if (!aCurrentCursor || aCurrentCursor->shape() != Qt::CrossCursor) {
+        QApplication::setOverrideCursor(PartSet_Tools::getOperationCursor());
   //#ifdef DEBUG_CURSOR
   //      qDebug("onEnterViewPort() : Qt::CrossCursor");
   //#endif
-  //    }
-  //  }
+      }
+    }
 
   if (!isNestedCreateOperation(getCurrentOperation(), activeSketch()))
     return;
@@ -266,12 +267,12 @@ void PartSet_SketcherMgr::onLeaveViewPort()
   return;
   #endif
 
-//  if (canChangeCursor(getCurrentOperation())) {
-//    QApplication::restoreOverrideCursor();
+  if (canChangeCursor(getCurrentOperation())) {
+    QApplication::restoreOverrideCursor();
 //#ifdef DEBUG_CURSOR
 //    qDebug("onLeaveViewPort() : None");
 //#endif
-//  }
+  }
 
   if (!isNestedCreateOperation(getCurrentOperation(), activeSketch()))
     return;
@@ -348,11 +349,35 @@ void PartSet_SketcherMgr::onAfterValuesChangedInPropertyPanel()
 }
 */
 
+bool PartSet_SketcherMgr::isDragModeCreation() const
+{
+  ModuleBase_Operation* aOp = getCurrentOperation();
+  if (!aOp)
+    return false;
+  bool aUserPref = Config_PropManager::boolean(SKETCH_TAB_NAME, "create_by_dragging");
+  if (!aUserPref)
+    return false;
+  QString aId = aOp->id();
+  // Acceptable features;
+  QStringList aList;
+  aList << "SketchLine" << "SketchMacroCircle" << "SketchMacroArc" <<
+    "SketchMacroEllipse" << "SketchMacroEllipticArc" << "SketchRectangle";
+  return aList.contains(aId);
+}
+
+static bool MyModeByDrag = false;
+static bool MyMultiselectionState = true;
+
 void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
 {
+  MyModeByDrag = isDragModeCreation();
+
   // Clear dragging mode
   myIsDragging = false;
 
+  myMousePoint.setX(theEvent->x());
+  myMousePoint.setY(theEvent->y());
+
   if (myModule->sketchReentranceMgr()->processMousePressed(theWnd, theEvent))
     return;
   //get2dPoint(theWnd, theEvent, myClickedPoint);
@@ -398,9 +423,24 @@ void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseE
       return;
 
     // Ignore creation sketch operation
-    if ((!isSketcher) && (!isEditing))
+    if ((!isSketcher) && (!isEditing)) {
+      if (MyModeByDrag) {
+        ModuleBase_ModelWidget* anActiveWidget = getActiveWidget();
+        PartSet_MouseProcessor* aProcessor = dynamic_cast<PartSet_MouseProcessor*>(anActiveWidget);
+        if (aProcessor) {
+          MyMultiselectionState = aViewer->isMultiSelectionEnabled();
+          aViewer->enableMultiselection(false);
+          myIsDragging = true;
+          ModuleBase_ISelection* aSelection = aWorkshop->selection();
+          QList<ModuleBase_ViewerPrsPtr> aPreSelected = aSelection->getHighlighted();
+          if (!aPreSelected.empty())
+            aProcessor->setPreSelection(aPreSelected.first(), theWnd, theEvent);
+          else
+            aProcessor->mouseReleased(theWnd, theEvent);
+        }
+      }
       return;
-
+    }
     bool aHasShift = (theEvent->modifiers() & Qt::ShiftModifier);
     storeSelection(aHasShift ? ST_SelectAndHighlightType : ST_HighlightType, myCurrentSelection);
 
@@ -456,9 +496,10 @@ void PartSet_SketcherMgr::onMousePressed(ModuleBase_IViewWindow* theWnd, QMouseE
           }
         }
       }
-      else
-        isRelaunchEditing = !myCurrentSelection.contains(aSPFeature);
-
+      else {
+        if (myCurrentSelection.size() > 1)
+          isRelaunchEditing = !myCurrentSelection.contains(aSPFeature);
+      }
       if (isRelaunchEditing)
         aFOperation->commit();
 
@@ -504,12 +545,13 @@ void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouse
   if (!myIsMouseOverViewProcessed) {
     return;
   }
-  //if (!aViewer->canDragByMouse())
-  //  return;
+
   ModuleBase_OperationFeature* aOp =
     dynamic_cast<ModuleBase_OperationFeature*>(getCurrentOperation());
+  bool isEditing = false;
   if (aOp) {
-    bool aStartNoDragOperation = !aViewer->canDragByMouse() && aOp->isEditOperation();
+    isEditing = aOp->isEditOperation();
+    bool aStartNoDragOperation = !aViewer->canDragByMouse() && isEditing;
     if (aStartNoDragOperation || myNoDragMoving) {
       // Process edit operation without dragging
       if (myCurrentSelection.size() > 0)
@@ -539,11 +581,31 @@ void PartSet_SketcherMgr::onMouseReleased(ModuleBase_IViewWindow* theWnd, QMouse
     }
   }
 
-
   ModuleBase_ModelWidget* anActiveWidget = getActiveWidget();
   PartSet_MouseProcessor* aProcessor = dynamic_cast<PartSet_MouseProcessor*>(anActiveWidget);
-  if (aProcessor)
-    aProcessor->mouseReleased(theWnd, theEvent);
+  if (aProcessor) {
+    ModuleBase_ISelection* aSelection = aWorkshop->selection();
+    QList<ModuleBase_ViewerPrsPtr> aPreSelected = aSelection->getHighlighted();
+    if (MyModeByDrag && !aPreSelected.empty() && !isEditing)
+      aProcessor->setPreSelection(aPreSelected.first(), theWnd, theEvent);
+    else
+      aProcessor->mouseReleased(theWnd, theEvent);
+  }
+  if (MyModeByDrag && aOp) {
+    QString aOpId = aOp->id();
+    if (aOpId == "Sketch")
+      return;
+    QPoint aPnt(theEvent->x(), theEvent->y());
+    anActiveWidget = getActiveWidget();
+    if ((aPnt == myMousePoint) && anActiveWidget) {
+      aOp->abort();
+      return;
+    }
+    bool aCanRestart = !anActiveWidget && !isEditing;
+    if (aCanRestart) {
+      module()->launchOperation(aOpId, true);
+    }
+  }
 }
 
 void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEvent* theEvent)
@@ -561,6 +623,7 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
     qDebug(QString("%1").arg(anInfo.size()).arg(anInfoStr).toStdString().c_str());
   }
 #endif
+
   if (myModule->sketchReentranceMgr()->processMouseMoved(theWnd, theEvent))
     return;
 
@@ -640,26 +703,26 @@ void PartSet_SketcherMgr::onMouseMoved(ModuleBase_IViewWindow* theWnd, QMouseEve
     for (; anIt != aLast; anIt++) {
       FeaturePtr aFeature = anIt.key();
 
-      std::set<AttributePtr> anAttributes = anIt.value().myAttributes;
+      std::map<AttributePtr, int> anAttributes = anIt.value().myAttributes;
       // Process selection by attribute: the priority to the attribute
       if (!anAttributes.empty()) {
-        std::set<AttributePtr>::const_iterator anAttIt = anAttributes.begin(),
+        std::map<AttributePtr, int>::const_iterator anAttIt = anAttributes.begin(),
           anAttLast = anAttributes.end();
         for (; anAttIt != anAttLast; anAttIt++) {
-          AttributePtr anAttr = *anAttIt;
+          AttributePtr anAttr = anAttIt->first;
           if (anAttr.get() == NULL)
             continue;
           std::string aAttrId = anAttr->id();
           DataPtr aData = aFeature->data();
           if (aData->isValid()) {
-            std::shared_ptr<GeomDataAPI_Point2D> aPoint =
-              std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aData->attribute(aAttrId));
-            if (aPoint.get() != NULL) {
+            AttributePtr aPoint = aData->attribute(aAttrId);
+            if (aPoint->attributeType() == GeomDataAPI_Point2D::typeId() ||
+                aPoint->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
               bool isImmutable = aPoint->setImmutable(true);
 
               std::shared_ptr<ModelAPI_ObjectMovedMessage> aMessage = std::shared_ptr
                 <ModelAPI_ObjectMovedMessage>(new ModelAPI_ObjectMovedMessage(this));
-              aMessage->setMovedAttribute(aPoint);
+              aMessage->setMovedAttribute(aPoint, anAttIt->second);
               aMessage->setOriginalPosition(anOriginalPosition);
               aMessage->setCurrentPosition(aCurrentPosition);
               Events_Loop::loop()->send(aMessage);
@@ -1216,30 +1279,30 @@ 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));
+void PartSet_SketcherMgr::startNestedSketch(ModuleBase_Operation* theOperation)
+{
+  if (canChangeCursor(theOperation) && myIsMouseOverWindow) {
+    QCursor* aCurrentCursor = QApplication::overrideCursor();
+    if (!aCurrentCursor || aCurrentCursor->shape() != Qt::CrossCursor) {
+      QApplication::setOverrideCursor(PartSet_Tools::getOperationCursor());
 //#ifdef DEBUG_CURSOR
 //      qDebug("startNestedSketch() : Qt::CrossCursor");
 //#endif
-//    }
-//  }
-//}
+    }
+  }
+}
 
 void PartSet_SketcherMgr::stopNestedSketch(ModuleBase_Operation* theOperation)
 {
   myIsMouseOverViewProcessed = true;
   operationMgr()->onValidateOperation();
   // when sketch nested operation is stopped the cursor should be restored unconditionally
-  //if (canChangeCursor(theOperation)) {
-    //QApplication::restoreOverrideCursor();
+  if (canChangeCursor(theOperation)) {
+    QApplication::restoreOverrideCursor();
 #ifdef DEBUG_CURSOR
     qDebug("stopNestedSketch() : None");
 #endif
-  //}
+  }
   /// improvement to deselect automatically all eventual selected objects, when
   // returning to the neutral point of the Sketcher
   bool isClearSelectionPossible = true;
@@ -1685,7 +1748,7 @@ void PartSet_SketcherMgr::getSelectionOwners(const FeaturePtr& theFeature,
 
   FeatureToSelectionMap::const_iterator anIt = theSelection.find(theFeature);
   SelectionInfo anInfo = anIt.value();
-  std::set<AttributePtr> aSelectedAttributes = anInfo.myAttributes;
+  std::map<AttributePtr, int> aSelectedAttributes = anInfo.myAttributes;
   std::set<ResultPtr> aSelectedResults = anInfo.myResults;
 
   ModuleBase_IViewer* aViewer = theWorkshop->viewer();
@@ -1737,10 +1800,10 @@ void PartSet_SketcherMgr::getSelectionOwners(const FeaturePtr& theFeature,
       const TopoDS_Shape& aShape = anOwner->Shape();
       TopAbs_ShapeEnum aShapeType = aShape.ShapeType();
       if (aShapeType == TopAbs_VERTEX) {
-        AttributePtr aPntAttr =
+        std::pair<AttributePtr, int> aPntAttrIndex =
           PartSet_Tools::findAttributeBy2dPoint(theFeature, aShape, theSketch);
-        if (aPntAttr.get() != NULL &&
-            aSelectedAttributes.find(aPntAttr) != aSelectedAttributes.end())
+        if (aPntAttrIndex.first.get() != NULL &&
+            aSelectedAttributes.find(aPntAttrIndex.first) != aSelectedAttributes.end())
           theOwnersToSelect.Add(anOwner);
         else if (isSameShape && anInfo.myLocalSelectedShapes.Contains(aShape)) {
           theOwnersToSelect.Add(anOwner);
@@ -2019,8 +2082,9 @@ void PartSet_SketcherMgr::updateBySketchParameters(
       if (aPrevState != theState) {
         /// call all sketch features redisplay, the expression state will be corrected in customize
         /// of distance presentation
-        SketcherPrs_Tools::
-          sendExpressionShownEvent(myIsConstraintsShown[PartSet_Tools::Expressions]);
+        SketcherPrs_Tools::ParameterStyle aStyle = myIsConstraintsShown[PartSet_Tools::Expressions]
+          ? SketcherPrs_Tools::ParameterText : SketcherPrs_Tools::ParameterValue;
+        SketcherPrs_Tools::setParameterStyle(aStyle);
         Events_ID anEventId = Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY);
         PartSet_Tools::sendSubFeaturesEvent(myCurrentSketch, anEventId);
       }
@@ -2269,8 +2333,9 @@ void PartSet_SketcherMgr::customizeSketchPresentation(const ObjectPtr& theObject
   if (aShapeType != 6/*an edge*/ && aShapeType != 7/*a vertex*/ && aShapeType != 0/*compound*/)
     return;
 
+  int aWidth = Config_PropManager::integer("Visualization", "sketch_line_width");
   if (isExternal(aFeature)) {
-    thePrs->setWidth(1);
+    thePrs->setWidth(isIncludeToResult(aFeature)? aWidth : 1);
     return;
   }
   std::string aKind = aFeature->getKind();
@@ -2284,7 +2349,6 @@ void PartSet_SketcherMgr::customizeSketchPresentation(const ObjectPtr& theObject
       thePrs->setLineStyle(SketchPlugin_SketchEntity::SKETCH_LINE_STYLE_AUXILIARY());
     }
     else {
-      int aWidth = Config_PropManager::integer("Visualization", "sketch_line_width");
       thePrs->setWidth(aWidth);
       thePrs->setLineStyle(SketchPlugin_SketchEntity::SKETCH_LINE_STYLE());
     }
index 20ee6e6594dee9e42b0466dc1960b8c49021bc92..cd8e3380f64e76eec5fc23539b16a46824dafc61 100644 (file)
@@ -55,6 +55,7 @@
 #include <QObject>
 #include <QList>
 #include <QMap>
+#include <QPoint>
 
 #include <set>
 
@@ -137,7 +138,8 @@ public:
   /// Struct to define selection model information to store/restore selection
   struct SelectionInfo
   {
-    std::set<AttributePtr> myAttributes; /// the selected attributes
+    /// the selected attributes and indices of points if array
+    std::map<AttributePtr, int> myAttributes;
     std::set<ResultPtr> myResults; /// the selected results
     TopoDS_Shape myFirstResultShape; /// the first shape of feature result
     TopTools_MapOfShape myLocalSelectedShapes; /// shapes of local selection
@@ -219,7 +221,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
@@ -380,6 +382,9 @@ public:
   */
   virtual void processEvent(const std::shared_ptr<Events_Message>& theMessage);
 
+  /// Returns true if current mode of objects creation is by drag mouse
+  bool isDragModeCreation() const;
+
 
 public slots:
   /// Process sketch plane selected event
@@ -509,6 +514,8 @@ private:
   QMap<ResultPtr, Handle(AIS_Shape)> myPointsHighlight;
 
   bool myNoDragMoving;
+
+  QPoint myMousePoint;
 };
 
 
index d46c5d30dddae5eb37e7604f55e779e7877358f3..ff241a14139cf873b2e947cac1fc6cb069da1725 100644 (file)
@@ -361,6 +361,11 @@ void PartSet_SketcherReentrantMgr::onNoMoreWidgets(const std::string& thePreviou
 
   ModuleBase_OperationFeature* aFOperation = dynamic_cast<ModuleBase_OperationFeature*>
                                                        (myWorkshop->currentOperation());
+  if (module()->sketchMgr()->isDragModeCreation()) {
+    if (aFOperation && myIsAutoConstraints)
+      addConstraints(aFOperation->feature());
+    return;
+  }
   if (!myWorkshop->module()->getFeatureError(aFOperation->feature()).isEmpty())
     return;
 
index fbb50228faf57290a384d347d72b2e8b0b0dffec..09f2e143726776d661a379a62fd906eacffb590c 100644 (file)
@@ -49,6 +49,7 @@
 #include <GeomDataAPI_Point.h>
 #include <GeomDataAPI_Dir.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 #include <GeomAPI_Pln.h>
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_Pnt.h>
@@ -443,9 +444,9 @@ GeomShapePtr PartSet_Tools::findShapeBy2DPoint(const AttributePtr& theAttribute,
               // attribute, returns the shape
               PartSet_Module* aModule = dynamic_cast<PartSet_Module*>(theWorkshop->module());
               PartSet_SketcherMgr* aSketchMgr = aModule->sketchMgr();
-              AttributePtr aPntAttr = PartSet_Tools::findAttributeBy2dPoint(anAttributeFeature,
-                                                        aBRepShape, aSketchMgr->activeSketch());
-              if (aPntAttr.get() != NULL && aPntAttr == theAttribute) {
+              std::pair<AttributePtr, int> aPntAttrIndex = PartSet_Tools::findAttributeBy2dPoint(
+                  anAttributeFeature, aBRepShape, aSketchMgr->activeSketch());
+              if (aPntAttrIndex.first.get() != NULL && aPntAttrIndex.first == theAttribute) {
                 aShape = std::shared_ptr<GeomAPI_Shape>(new GeomAPI_Shape);
                 aShape->setImpl(new TopoDS_Shape(aBRepShape));
                 break;
@@ -483,6 +484,24 @@ std::shared_ptr<GeomAPI_Pnt2d> PartSet_Tools::getPnt2d(QMouseEvent* theEvent,
   return std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(aX, anY));
 }
 
+std::shared_ptr<GeomAPI_Pnt2d> PartSet_Tools::getPnt2d(const Handle(V3d_View)& theView,
+                                                       const TopoDS_Shape& theShape,
+                                                       const FeaturePtr& theSketch)
+{
+  GeomPnt2dPtr aPoint2D;
+  if (!theShape.IsNull() && theShape.ShapeType() == TopAbs_VERTEX) {
+    const TopoDS_Vertex& aVertex = TopoDS::Vertex(theShape);
+    if (!aVertex.IsNull()) {
+      // the case when the point is taken from the existing vertex
+      gp_Pnt aPoint = BRep_Tool::Pnt(aVertex);
+      double aX, aY;
+      PartSet_Tools::convertTo2D(aPoint, theSketch, theView, aX, aY);
+      aPoint2D.reset(new GeomAPI_Pnt2d(aX, aY));
+    }
+  }
+  return aPoint2D;
+}
+
 FeaturePtr findFirstCoincidenceByData(const DataPtr& theData,
                                       std::shared_ptr<GeomAPI_Pnt2d> thePoint)
 {
@@ -630,12 +649,44 @@ std::shared_ptr<GeomAPI_Pnt2d> PartSet_Tools::getCoincedencePoint(FeaturePtr the
   return aPnt;
 }
 
-AttributePtr PartSet_Tools::findAttributeBy2dPoint(ObjectPtr theObj,
-                                                   const TopoDS_Shape theShape,
-                                                   FeaturePtr theSketch)
+class PointWrapper
+{
+public:
+  PointWrapper(AttributePtr theAttribute)
+    : myPoint(std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute)),
+      myArray(std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(theAttribute))
+  {}
+
+  int size() const { return myPoint.get() ? 1 : (myArray.get() ? myArray->size() : 0); }
+
+  GeomPointPtr point(int theIndex, FeaturePtr theSketch)
+  {
+    GeomPnt2dPtr aP2d;
+    if (myPoint.get())
+      aP2d = myPoint->pnt();
+    else if (myArray.get())
+      aP2d = myArray->pnt(theIndex);
+
+    GeomPointPtr aP3d;
+    if (aP2d.get())
+      aP3d = PartSet_Tools::convertTo3D(aP2d->x(), aP2d->y(), theSketch);
+    return aP3d;
+  }
+
+  bool isArray() const { return myArray.get(); }
+
+private:
+  AttributePoint2DPtr myPoint;
+  AttributePoint2DArrayPtr myArray;
+};
+
+std::pair<AttributePtr, int> PartSet_Tools::findAttributeBy2dPoint(ObjectPtr theObj,
+                                                                   const TopoDS_Shape theShape,
+                                                                   FeaturePtr theSketch)
 {
 
   AttributePtr anAttribute;
+  int aPointIndex = -1;
   FeaturePtr aFeature = ModelAPI_Feature::feature(theObj);
   if (aFeature) {
     if (theShape.ShapeType() == TopAbs_VERTEX) {
@@ -648,29 +699,32 @@ AttributePtr PartSet_Tools::findAttributeBy2dPoint(ObjectPtr theObj,
         // find the given point in the feature attributes
         std::list<AttributePtr> anAttiributes =
           aFeature->data()->attributes(GeomDataAPI_Point2D::typeId());
+        std::list<AttributePtr> anArrays =
+          aFeature->data()->attributes(GeomDataAPI_Point2DArray::typeId());
+        anAttiributes.insert(anAttiributes.end(), anArrays.begin(), anArrays.end());
+
         std::list<AttributePtr>::const_iterator anIt = anAttiributes.begin(),
                                                 aLast = anAttiributes.end();
         double aMinDistance = 1.e-6; // searching for point with minimal distance and < 1.e-6
         for (; anIt != aLast && !anAttribute; anIt++) {
-          std::shared_ptr<GeomDataAPI_Point2D> aCurPoint =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(*anIt);
-          if (!aCurPoint->isInitialized())
-            continue;
-
-          std::shared_ptr<GeomAPI_Pnt> aPnt =
-            convertTo3D(aCurPoint->x(), aCurPoint->y(), theSketch);
-          if (aPnt) {
-            double aDistance = aPnt->distance(aValue);
-            if (aDistance < aMinDistance) {
-              anAttribute = aCurPoint;
-              aMinDistance = aPnt->distance(aValue);
+          PointWrapper aWrapper(*anIt);
+          for (int anIndex = 0, aSize = aWrapper.size(); anIndex < aSize; ++anIndex) {
+            std::shared_ptr<GeomAPI_Pnt> aPnt = aWrapper.point(anIndex, theSketch);
+            if (aPnt) {
+              double aDistance = aPnt->distance(aValue);
+              if (aDistance < aMinDistance) {
+                anAttribute = *anIt;
+                if (aWrapper.isArray())
+                  aPointIndex = anIndex;
+                aMinDistance = aPnt->distance(aValue);
+              }
             }
           }
         }
       }
     }
   }
-  return anAttribute;
+  return std::pair<AttributePtr, int>(anAttribute, aPointIndex);
 }
 
 void PartSet_Tools::sendSubFeaturesEvent(const CompositeFeaturePtr& theComposite,
@@ -851,3 +905,17 @@ double PartSet_Tools::getDefaultTransparency()
 {
   return Config_PropManager::integer("Visualization", "shaper_default_transparency") / 100.;
 }
+
+QCursor PartSet_Tools::getOperationCursor()
+{
+  int aId = Config_PropManager::integer(SKETCH_TAB_NAME, "operation_cursor");
+  switch (aId) {
+  case 0:
+    return QCursor(Qt::ArrowCursor);
+  case 1:
+    return QCursor(Qt::CrossCursor);
+  case 2:
+    return QCursor(Qt::PointingHandCursor);
+  }
+  return QCursor();
+}
index 7f1c236abb474dc25a51394062dce820cc262cfe..574ecc5b4221c00a379af6b1f7e5f9b3961cf04d 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <QPoint>
 #include <QList>
+#include <QCursor>
 
 #include <ModelAPI_CompositeFeature.h>
 #include <ModelAPI_Object.h>
@@ -192,9 +193,11 @@ public:
   * \param theObj - an object
   * \param theShape - a Shape
   * \param theSketch - a Sketch to get a plane of converting to 2d
+  * \return Found attribute and index of point if the attribute is an array
   */
-  static AttributePtr findAttributeBy2dPoint(ObjectPtr theObj, const TopoDS_Shape theShape,
-                                             FeaturePtr theSketch);
+  static std::pair<AttributePtr, int> findAttributeBy2dPoint(ObjectPtr theObj,
+                                                             const TopoDS_Shape theShape,
+                                                             FeaturePtr theSketch);
 
   /**
   * Finds an attribute value in attribute reference attribute value
@@ -215,7 +218,7 @@ public:
 
   /**
   * Convertes parameters into a geom point
-  * \theEvent a Qt event to find mouse position
+  * \param theEvent a Qt event to find mouse position
   * \param theWindow view window to define eye of view
   * \param theSketch to convert 3D point coordinates into coorditates of the sketch plane
   */
@@ -223,6 +226,16 @@ public:
                                                  ModuleBase_IViewWindow* theWindow,
                                                  const FeaturePtr& theSketch);
 
+  /** Returns point 2d from selected shape
+   *  \param theView a view window
+   *  \param theShape a vertex shape
+   *  \param theX an output value of X coordinate
+   *  \param theY an output value of Y coordinate
+   */
+  static std::shared_ptr<GeomAPI_Pnt2d> getPnt2d(const Handle(V3d_View)& theView,
+                                                 const TopoDS_Shape& theShape,
+                                                 const FeaturePtr& theSketch);
+
   /**
   * Gets all references to the feature, take coincidence constraint features, get point 2d attributes
   * and compare the point value to be equal with the given. Returns the first feature, which has
@@ -317,6 +330,11 @@ public:
   * Returns default transparency value
   */
   static double getDefaultTransparency();
+
+  /**
+  * Returns cursor according to (SKETCH_TAB_NAME, "operation_cursor") property value
+  */
+  static QCursor getOperationCursor();
 };
 
 #endif
diff --git a/src/PartSet/PartSet_WidgetBSplinePoints.cpp b/src/PartSet/PartSet_WidgetBSplinePoints.cpp
new file mode 100644 (file)
index 0000000..0678e82
--- /dev/null
@@ -0,0 +1,651 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <PartSet_WidgetBSplinePoints.h>
+
+#include <PartSet_CenterPrs.h>
+#include <PartSet_ExternalObjectsMgr.h>
+#include <PartSet_Module.h>
+#include <PartSet_SketcherReentrantMgr.h>
+#include <PartSet_WidgetPoint2d.h>
+
+#include <XGUI_Tools.h>
+#include <XGUI_Workshop.h>
+#include <XGUI_Displayer.h>
+
+#include <ModuleBase_ISelection.h>
+#include <ModuleBase_IViewer.h>
+#include <ModuleBase_IViewWindow.h>
+#include <ModuleBase_LabelValue.h>
+#include <ModuleBase_Tools.h>
+#include <ModuleBase_ViewerPrs.h>
+#include <ModuleBase_WidgetValidator.h>
+#include <ModuleBase_WidgetValidated.h>
+
+#include <Config_Keywords.h>
+#include <Config_WidgetAPI.h>
+
+#include <Events_Loop.h>
+
+#include <ModelAPI_Events.h>
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeRefAttrList.h>
+#include <ModelAPI_CompositeFeature.h>
+
+#include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <GeomAPI_Pnt2d.h>
+#include <GeomAPI_IPresentable.h>
+
+#include <SketchPlugin_Feature.h>
+
+#include <QGridLayout>
+#include <QGroupBox>
+#include <QMouseEvent>
+#include <QGraphicsEffect>
+#include <QScrollArea>
+
+static const double MaxCoordinate = 1e12;
+
+static bool IsPointCreated = false;
+
+
+PartSet_WidgetBSplinePoints::PartSet_WidgetBSplinePoints(QWidget* theParent,
+                                             ModuleBase_IWorkshop* theWorkshop,
+                                             const Config_WidgetAPI* theData)
+: ModuleBase_ModelWidget(theParent, theData), myWorkshop(theWorkshop),
+  myValueIsCashed(false), myIsFeatureVisibleInCash(true),
+  myXValueInCash(0), myYValueInCash(0),
+  myPointIndex(0), myFinished(false)
+{
+  myRefAttribute = theData->getProperty("reference_attribute");
+  QVBoxLayout* aMainLayout = new QVBoxLayout(this);
+  ModuleBase_Tools::zeroMargins(aMainLayout);
+
+  // the control should accept the focus, so the boolean flag is corrected to be true
+  myIsObligatory = true;
+  QString aPageName = translate(theData->getProperty(CONTAINER_PAGE_NAME));
+  myBox = new QGroupBox(aPageName, theParent);
+  myBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  myBox->setFlat(false);
+  aMainLayout->addWidget(myBox);
+
+  bool aAcceptVariables = theData->getBooleanAttribute(DOUBLE_WDG_ACCEPT_EXPRESSIONS, true);
+
+  // B-spline weights attribute
+  myWeightsAttr = theData->getProperty("weights");
+
+  QVBoxLayout* aLayout = new QVBoxLayout(myBox);
+  ModuleBase_Tools::adjustMargins(aLayout);
+
+  myScrollArea = new QScrollArea(myBox);
+  myScrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+  myScrollArea->setWidgetResizable(true);
+  myScrollArea->setFrameStyle(QFrame::NoFrame);
+  aLayout->addWidget(myScrollArea);
+
+  QWidget* aContainer = new QWidget(myScrollArea);
+  QVBoxLayout* aBoxLay = new QVBoxLayout(aContainer);
+  aBoxLay->setContentsMargins(0, 0, 0, 0);
+
+  myGroupBox = new QWidget(aContainer);
+  QGridLayout* aGroupLay = new QGridLayout(myGroupBox);
+  ModuleBase_Tools::adjustMargins(aGroupLay);
+  aGroupLay->setSpacing(4);
+  aGroupLay->setColumnStretch(1, 1);
+  createNextPoint();
+  aBoxLay->addWidget(myGroupBox);
+  aBoxLay->addStretch(1);
+
+  myScrollArea->setWidget(aContainer);
+
+  myWidgetValidator = new ModuleBase_WidgetValidator(this, myWorkshop);
+  myExternalObjectMgr = new PartSet_ExternalObjectsMgr(theData->getProperty("use_external"),
+                                         theData->getProperty("can_create_external"), true);
+}
+
+void PartSet_WidgetBSplinePoints::createNextPoint()
+{
+  storeCurentValue();
+
+  QGridLayout* aGroupLay = dynamic_cast<QGridLayout*>(myGroupBox->layout());
+  int row = (int)myXSpin.size();
+
+  QString aPoleStr = tr("Pole %1");
+  aPoleStr = aPoleStr.arg(myXSpin.size() + 1);
+
+  QGroupBox* aPoleGroupBox = new QGroupBox(aPoleStr, myGroupBox);
+  QGridLayout* aPoleLay = new QGridLayout(aPoleGroupBox);
+  ModuleBase_Tools::adjustMargins(aPoleLay);
+  aPoleLay->setSpacing(2);
+  aPoleLay->setColumnStretch(1, 1);
+
+  myXSpin.push_back(new ModuleBase_LabelValue(aPoleGroupBox, tr("X")));
+  aPoleLay->addWidget(myXSpin.back(), 0, 1);
+  myYSpin.push_back(new ModuleBase_LabelValue(aPoleGroupBox, tr("Y")));
+  aPoleLay->addWidget(myYSpin.back(), 1, 1);
+
+  aGroupLay->addWidget(aPoleGroupBox, row, 1);
+  IsPointCreated = true;
+}
+
+void PartSet_WidgetBSplinePoints::removeLastPoint()
+{
+  QGridLayout* aGroupLay = dynamic_cast<QGridLayout*>(myGroupBox->layout());
+  QWidget* aXSpin = myXSpin.back();
+  QWidget* aYSpin = myYSpin.back();
+  QWidget* aBox = myXSpin.back()->parentWidget();
+  myYSpin.pop_back();
+  myXSpin.pop_back();
+
+  aGroupLay->removeWidget(aXSpin);
+  aGroupLay->removeWidget(aYSpin);
+  aGroupLay->removeWidget(aBox);
+
+  aBox->deleteLater();
+
+  // update B-spline feature attributes
+  storeValueCustom();
+}
+
+bool PartSet_WidgetBSplinePoints::isValidSelectionCustom(const ModuleBase_ViewerPrsPtr& theValue)
+{
+  bool aValid = true;
+
+  PartSet_Module* aModule = dynamic_cast<PartSet_Module*>(myWorkshop->module());
+  if (aModule->sketchReentranceMgr()->isInternalEditActive())
+    return true; // when internal edit is started a new feature is created. I has not results, AIS
+
+  // the selection is not possible if the current feature has no presentation for the current
+  // attribute not in AIS not in results. If so, no object in current feature where make
+  // coincidence, so selection is not necessary
+  GeomShapePtr anAISShape;
+  GeomPresentablePtr aPrs = std::dynamic_pointer_cast<GeomAPI_IPresentable>(myFeature);
+  if (aPrs.get()) {
+    AISObjectPtr anAIS;
+    anAIS = aPrs->getAISObject(anAIS);
+    if (anAIS.get()) {
+      anAISShape = anAIS->getShape();
+    }
+  }
+  const std::list<std::shared_ptr<ModelAPI_Result> >& aResults = myFeature->results();
+  if (!anAISShape.get() && aResults.empty())
+    return true;
+
+  AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList();
+  if (aRefAttrList)
+    return isValidSelectionForAttribute_(theValue, myFeature->attribute(attributeID()));
+  return true;
+}
+
+bool PartSet_WidgetBSplinePoints::isValidSelectionForAttribute_(
+                                            const ModuleBase_ViewerPrsPtr& theValue,
+                                            const AttributePtr& theAttribute)
+{
+  bool aValid = false;
+
+  // stores the current values of the widget attribute
+  bool isFlushesActived, isAttributeSetInitializedBlocked, isAttributeSendUpdatedBlocked;
+
+  AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList();
+  ModuleBase_WidgetValidated::blockFeatureAttribute(aRefAttrList, myFeature, true,
+      isFlushesActived, isAttributeSetInitializedBlocked, isAttributeSendUpdatedBlocked);
+  myWidgetValidator->storeAttributeValue(aRefAttrList);
+
+  // saves the owner value to the widget attribute
+  aValid = setSelectionCustom(theValue);
+  if (aValid)
+    // checks the attribute validity
+    aValid = myWidgetValidator->isValidAttribute(theAttribute);
+
+  // restores the current values of the widget attribute
+  myWidgetValidator->restoreAttributeValue(aRefAttrList, aValid);
+  myExternalObjectMgr->removeExternal(sketch(), myFeature, myWorkshop, true);
+
+  ModuleBase_WidgetValidated::blockFeatureAttribute(aRefAttrList, myFeature, false,
+      isFlushesActived, isAttributeSetInitializedBlocked, isAttributeSendUpdatedBlocked);
+  return aValid;
+}
+
+bool PartSet_WidgetBSplinePoints::setSelectionCustom(const ModuleBase_ViewerPrsPtr& theValue)
+{
+  bool isDone = false;
+  GeomShapePtr aShape = theValue->shape();
+  if (aShape.get() && !aShape->isNull()) {
+    Handle(V3d_View) aView = myWorkshop->viewer()->activeView();
+    const TopoDS_Shape& aTDShape = aShape->impl<TopoDS_Shape>();
+    GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aTDShape, mySketch);
+    if (aPnt) {
+      fillRefAttribute(aPnt, theValue);
+      isDone = true;
+    }
+    else if (aTDShape.ShapeType() == TopAbs_EDGE) {
+      fillRefAttribute(theValue);
+      isDone = true;
+    }
+  }
+  return isDone;
+}
+
+static void fillLabels(std::vector<ModuleBase_LabelValue*>& theLabels, const double theValue)
+{
+  for (std::vector<ModuleBase_LabelValue*>::iterator anIt = theLabels.begin();
+       anIt != theLabels.end(); ++anIt)
+    (*anIt)->setValue(theValue);
+}
+
+bool PartSet_WidgetBSplinePoints::resetCustom()
+{
+  bool aDone = false;
+  if (!isUseReset() || isComputedDefault())
+    aDone = false;
+  else {
+    if (myValueIsCashed) {
+      // if the restored value should be hidden, aDone = true to set
+      // reset state for the widget in the parent
+      aDone = restoreCurentValue();
+      emit objectUpdated();
+    }
+    else {
+      // it is important to block the spin box control in order to do not through out the
+      // locking of the validating state.
+      fillLabels(myXSpin, 0.0);
+      fillLabels(myYSpin, 0.0);
+
+      storeValueCustom();
+      aDone = true;
+    }
+  }
+  return aDone;
+}
+
+PartSet_WidgetBSplinePoints::~PartSet_WidgetBSplinePoints()
+{
+  delete myExternalObjectMgr;
+}
+
+bool PartSet_WidgetBSplinePoints::setPoint(double theX, double theY)
+{
+  if (fabs(theX) >= MaxCoordinate || fabs(theY) >= MaxCoordinate)
+    return false;
+
+  myXSpin.back()->setValue(theX);
+  myYSpin.back()->setValue(theY);
+
+  storeValue();
+  return true;
+}
+
+void PartSet_WidgetBSplinePoints::storePolesAndWeights() const
+{
+  std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
+  AttributePoint2DArrayPtr aPointArray = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+      aData->attribute(attributeID()));
+  AttributeDoubleArrayPtr aWeightsArray = aData->realArray(myWeightsAttr);
+
+  int aSize = (int)myXSpin.size();
+  aPointArray->setSize(aSize);
+  aWeightsArray->setSize(aSize);
+
+  std::vector<ModuleBase_LabelValue*>::const_iterator aXIt = myXSpin.begin();
+  std::vector<ModuleBase_LabelValue*>::const_iterator aYIt = myYSpin.begin();
+  for (int anIndex = 0; aXIt != myXSpin.end() && aYIt != myYSpin.end(); ++anIndex, ++aXIt, ++aYIt)
+    aPointArray->setPnt(anIndex, (*aXIt)->value(), (*aYIt)->value());
+
+  double aWeight = Config_PropManager::real(SKETCH_TAB_NAME, "spline_weight");
+  for (int anIndex = 0; anIndex < aSize; ++anIndex)
+    aWeightsArray->setValue(anIndex, aWeight);
+}
+
+bool PartSet_WidgetBSplinePoints::storeValueCustom()
+{
+  std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
+  if (!aData || !aData->isValid()) // can be on abort of sketcher element
+    return false;
+  AttributePoint2DArrayPtr aPointArray = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+      aData->attribute(attributeID()));
+  AttributeDoubleArrayPtr aWeightsArray = aData->realArray(myWeightsAttr);
+
+  PartSet_WidgetBSplinePoints* that = (PartSet_WidgetBSplinePoints*) this;
+  bool isBlocked = that->blockSignals(true);
+  bool isImmutable = aPointArray->setImmutable(true);
+
+  if (myFeature->isMacro()) {
+    // Moving points of macro-features has been processed directly (without solver)
+    storePolesAndWeights();
+    updateObject(myFeature);
+
+  } else {
+    if (!aPointArray->isInitialized()) {
+      storePolesAndWeights();
+    }
+
+    std::shared_ptr<ModelAPI_ObjectMovedMessage> aMessage(
+        new ModelAPI_ObjectMovedMessage(this));
+    aMessage->setMovedAttribute(aPointArray, aPointArray->size() - 1);
+    aMessage->setOriginalPosition(aPointArray->pnt(aPointArray->size() - 1));
+    aMessage->setCurrentPosition(myXSpin.back()->value(), myYSpin.back()->value());
+    Events_Loop::loop()->send(aMessage);
+  }
+
+  aPointArray->setImmutable(isImmutable);
+  that->blockSignals(isBlocked);
+
+  return true;
+}
+
+bool PartSet_WidgetBSplinePoints::restoreValueCustom()
+{
+  std::shared_ptr<ModelAPI_Data> aData = myFeature->data();
+  AttributePoint2DArrayPtr aPointArray = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+      aData->attribute(attributeID()));
+  AttributeDoubleArrayPtr aWeightsArray = aData->realArray(myWeightsAttr);
+
+  if (aPointArray->isInitialized()) {
+    while (myXSpin.size() < aPointArray->size())
+      createNextPoint();
+
+    std::vector<ModuleBase_LabelValue*>::iterator aXIt = myXSpin.begin();
+    std::vector<ModuleBase_LabelValue*>::iterator aYIt = myYSpin.begin();
+    for (int anIndex = 0; aXIt != myXSpin.end() && aYIt != myYSpin.end();
+         ++anIndex, ++aXIt, ++aYIt) {
+      GeomPnt2dPtr aPoint = aPointArray->pnt(anIndex);
+      (*aXIt)->setValue(aPoint->x());
+      (*aYIt)->setValue(aPoint->y());
+    }
+  }
+  else {
+    if (myXSpin.empty())
+      createNextPoint();
+
+    myXSpin.back()->setValue(0.0);
+    myYSpin.back()->setValue(0.0);
+  }
+
+  return true;
+}
+
+static void storeArray(const std::vector<ModuleBase_LabelValue*>& theLabels,
+                       std::vector<double>& theValues)
+{
+  theValues.clear();
+  theValues.reserve(theLabels.size());
+  for (std::vector<ModuleBase_LabelValue*>::const_iterator anIt = theLabels.begin();
+       anIt != theLabels.end(); ++anIt)
+    theValues.push_back((*anIt)->value());
+}
+
+void PartSet_WidgetBSplinePoints::storeCurentValue()
+{
+  myValueIsCashed = true;
+  myIsFeatureVisibleInCash = XGUI_Displayer::isVisible(
+                       XGUI_Tools::workshop(myWorkshop)->displayer(), myFeature);
+
+  storeArray(myXSpin, myXValueInCash);
+  storeArray(myYSpin, myYValueInCash);
+}
+
+static void restoreArray(std::vector<double>& theCacheValues,
+                         std::vector<ModuleBase_LabelValue*>& theLabels)
+{
+  std::vector<double>::iterator aCIt = theCacheValues.begin();
+  std::vector<ModuleBase_LabelValue*>::iterator anIt = theLabels.begin();
+  for (; anIt != theLabels.end(); ++anIt) {
+    if (aCIt != theCacheValues.end())
+      (*anIt)->setValue(*aCIt++);
+    else
+      (*anIt)->setValue(0.0);
+  }
+  theCacheValues.clear();
+}
+
+bool PartSet_WidgetBSplinePoints::restoreCurentValue()
+{
+  bool aRestoredAndHidden = true;
+
+  bool isVisible = myIsFeatureVisibleInCash;
+
+  myValueIsCashed = false;
+  myIsFeatureVisibleInCash = true;
+  // fill the control widgets by the cashed value
+  restoreArray(myXValueInCash, myXSpin);
+  restoreArray(myYValueInCash, myYSpin);
+
+  // store value to the model
+  storeValueCustom();
+  if (isVisible) {
+    setValueState(Stored);
+    aRestoredAndHidden = false;
+  }
+  else
+    aRestoredAndHidden = true;
+
+  return aRestoredAndHidden;
+}
+
+QList<QWidget*> PartSet_WidgetBSplinePoints::getControls() const
+{
+  QList<QWidget*> aControls;
+  aControls.append(myScrollArea);
+  return aControls;
+}
+
+void PartSet_WidgetBSplinePoints::selectionModes(int& theModuleSelectionModes, QIntList& theModes)
+{
+  theModuleSelectionModes = -1;
+  theModes << TopAbs_VERTEX;
+  theModes << TopAbs_EDGE;
+}
+
+void PartSet_WidgetBSplinePoints::deactivate()
+{
+  // the value of the control should be stored to model if it was not
+  // initialized yet. It is important when we leave this control by Tab key.
+  // It should not be performed by the widget activation as the preview
+  // is visualized with default value. Line point is moved to origin.
+  AttributePtr anAttribute = myFeature->data()->attribute(attributeID());
+  if (anAttribute && !anAttribute->isInitialized())
+    storeValue();
+
+  ModuleBase_ModelWidget::deactivate();
+}
+
+void PartSet_WidgetBSplinePoints::mouseReleased(ModuleBase_IViewWindow* theWindow,
+                                                QMouseEvent* theEvent)
+{
+  // the contex menu release by the right button should not be processed by this widget
+  if (theEvent->button() != Qt::LeftButton)
+    return;
+
+  ModuleBase_ISelection* aSelection = myWorkshop->selection();
+  Handle(V3d_View) aView = theWindow->v3dView();
+
+  QList<ModuleBase_ViewerPrsPtr> aList = aSelection->getSelected(ModuleBase_ISelection::Viewer);
+  ModuleBase_ViewerPrsPtr aFirstValue =
+    aList.size() > 0 ? aList.first() : ModuleBase_ViewerPrsPtr();
+  if (!aFirstValue.get() && myPreSelected.get()) {
+    aFirstValue = myPreSelected;
+  }
+
+  TopoDS_Shape aSelectedShape;
+  ObjectPtr aSelectedObject;
+
+  // if we have selection and use it
+  if (aFirstValue.get() && isValidSelectionCustom(aFirstValue) &&
+      aFirstValue->shape().get()) { // Trihedron Axis may be selected, but shape is empty
+    GeomShapePtr aGeomShape = aFirstValue->shape();
+    aSelectedShape = aGeomShape->impl<TopoDS_Shape>();
+    aSelectedObject = aFirstValue->object();
+
+    FeaturePtr aSelectedFeature = ModelAPI_Feature::feature(aSelectedObject);
+    std::shared_ptr<SketchPlugin_Feature> aSPFeature;
+    if (aSelectedFeature.get())
+      aSPFeature = std::dynamic_pointer_cast<SketchPlugin_Feature>(aSelectedFeature);
+
+    bool isSketchExternalFeature = aSPFeature.get() && aSPFeature->isExternal();
+    if ((!aSPFeature && !aSelectedShape.IsNull()) || isSketchExternalFeature) {
+      ObjectPtr aFixedObject =
+          PartSet_Tools::findFixedObjectByExternal(aSelectedShape, aSelectedObject, mySketch);
+      if (aFixedObject)
+        aSelectedObject = aFixedObject;
+      else if (!isSketchExternalFeature) {
+        FeaturePtr aCreatedFeature;
+        aSelectedObject = PartSet_Tools::createFixedObjectByExternal(
+            aGeomShape, aSelectedObject, mySketch, false, aCreatedFeature);
+      }
+    }
+  }
+  // The selection could be a center of an external circular object
+  else if (aFirstValue.get() && (!aFirstValue->interactive().IsNull())) {
+    Handle(PartSet_CenterPrs) aAIS =
+        Handle(PartSet_CenterPrs)::DownCast(aFirstValue->interactive());
+    if (!aAIS.IsNull()) {
+      gp_Pnt aPntComp = aAIS->Component()->Pnt();
+      GeomVertexPtr aVertPtr(new GeomAPI_Vertex(aPntComp.X(), aPntComp.Y(), aPntComp.Z()));
+      aSelectedShape = aVertPtr->impl<TopoDS_Shape>();
+
+      aSelectedObject =
+          PartSet_Tools::findFixedObjectByExternal(aSelectedShape, aAIS->object(), mySketch);
+      if (!aSelectedObject.get())
+      {
+        FeaturePtr aCreatedFeature;
+        aSelectedObject = PartSet_Tools::createFixedByExternalCenter(aAIS->object(), aAIS->edge(),
+            aAIS->centerType(), mySketch, false, aCreatedFeature);
+      }
+    }
+  }
+
+  GeomPnt2dPtr aSelectedPoint = PartSet_Tools::getPnt2d(aView, aSelectedShape, mySketch);
+  if (!aSelectedPoint) {
+    aSelectedPoint = PartSet_Tools::getPnt2d(theEvent, theWindow, mySketch);
+    setValueState(Stored); // in case of edge selection, Apply state should also be updated
+  }
+  if (aSelectedObject)
+    fillRefAttribute(aSelectedObject);
+  else
+    fillRefAttribute(aSelectedPoint, aFirstValue);
+
+  // next pole of B-spline
+  createNextPoint();
+}
+
+void PartSet_WidgetBSplinePoints::mouseMoved(ModuleBase_IViewWindow* theWindow,
+                                             QMouseEvent* theEvent)
+{
+  PartSet_Module* aModule = dynamic_cast<PartSet_Module*>(myWorkshop->module());
+
+  if (myFinished || isEditingMode() || aModule->sketchReentranceMgr()->isInternalEditActive())
+    return;
+
+  gp_Pnt aPoint = PartSet_Tools::convertClickToPoint(theEvent->pos(), theWindow->v3dView());
+
+  double aX = 0, aY = 0;
+  PartSet_Tools::convertTo2D(aPoint, mySketch, theWindow->v3dView(), aX, aY);
+  if (myState != ModifiedInViewer)
+    storeCurentValue();
+  // we need to block the value state change
+  bool isBlocked = blockValueState(true);
+  setPoint(aX, aY);
+  blockValueState(isBlocked);
+  setValueState(ModifiedInViewer);
+
+  if (IsPointCreated) {
+    QPoint aPnt = myGroupBox->geometry().bottomLeft();
+    myScrollArea->ensureVisible(aPnt.x(), aPnt.y());
+    IsPointCreated = false;
+  }
+}
+
+bool PartSet_WidgetBSplinePoints::processEscape()
+{
+  bool isProcessed = !isEditingMode();
+  if (isProcessed) {
+    // remove widgets corrsponding to the last pole/weight of B-spline
+    removeLastPoint();
+    myFinished = true;
+
+    emit focusOutWidget(this);
+  }
+  return isProcessed;
+}
+
+bool PartSet_WidgetBSplinePoints::useSelectedShapes() const
+{
+  return true;
+}
+
+AttributeRefAttrListPtr PartSet_WidgetBSplinePoints::attributeRefAttrList() const
+{
+  if (myRefAttribute.empty())
+    return AttributeRefAttrListPtr();
+
+  AttributePtr anAttributeRef = feature()->attribute(myRefAttribute);
+  if (!anAttributeRef.get())
+    return AttributeRefAttrListPtr();
+
+  return std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(anAttributeRef);
+}
+
+void PartSet_WidgetBSplinePoints::fillRefAttribute(GeomPnt2dPtr theClickedPoint,
+                              const std::shared_ptr<ModuleBase_ViewerPrs>& theValue)
+{
+  AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList();
+  if (!aRefAttrList.get())
+    return;
+
+  FeaturePtr aFeature = feature();
+  std::string anAttribute = attributeID();
+
+  if (aFeature.get()) {
+    AttributePoint2DPtr aClickedFeaturePoint =
+        PartSet_WidgetPoint2D::findFirstEqualPointInSketch(mySketch, aFeature, theClickedPoint);
+    if (aClickedFeaturePoint.get())
+      aRefAttrList->append(aClickedFeaturePoint);
+    else
+      fillRefAttribute(theValue);
+  }
+}
+
+void PartSet_WidgetBSplinePoints::fillRefAttribute(const ModuleBase_ViewerPrsPtr& theValue)
+{
+  ObjectPtr anObject;
+  if (theValue)
+    anObject = getGeomSelection(theValue);
+  fillRefAttribute(anObject);
+}
+
+void PartSet_WidgetBSplinePoints::fillRefAttribute(const ObjectPtr& theObject)
+{
+  AttributeRefAttrListPtr aRefAttrList = attributeRefAttrList();
+  if (aRefAttrList.get())
+    aRefAttrList->append(theObject);
+}
+
+ObjectPtr PartSet_WidgetBSplinePoints::getGeomSelection(const ModuleBase_ViewerPrsPtr& theValue)
+{
+  ObjectPtr anObject;
+  GeomShapePtr aShape;
+  ModuleBase_ISelection* aSelection = myWorkshop->selection();
+  anObject = aSelection->getResult(theValue);
+  aShape = aSelection->getShape(theValue);
+  myExternalObjectMgr->getGeomSelection(theValue, anObject, aShape, myWorkshop, sketch(), true);
+
+  return anObject;
+}
diff --git a/src/PartSet/PartSet_WidgetBSplinePoints.h b/src/PartSet/PartSet_WidgetBSplinePoints.h
new file mode 100644 (file)
index 0000000..8285ab9
--- /dev/null
@@ -0,0 +1,190 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef PartSet_WidgetBSplinePoints_H
+#define PartSet_WidgetBSplinePoints_H
+
+#include "PartSet.h"
+#include "PartSet_MouseProcessor.h"
+
+#include <ModuleBase_ModelWidget.h>
+
+#include <QObject>
+
+class GeomAPI_Pnt2d;
+class ModelAPI_CompositeFeature;
+class ModuleBase_LabelValue;
+class PartSet_ExternalObjectsMgr;
+class QGroupBox;
+class QScrollArea;
+
+/**\class PartSet_WidgetBSplinePoints
+ * \ingroup Modules
+ * \brief Implementation of model widget to provide widget to input a list of 2D poles
+ *        of B-spline curve in association with weights
+ * In XML can be defined as following:
+ * \code
+ * <sketch-bspline_selector id="poles" weights="weights"/>
+ * \endcode
+ */
+class PARTSET_EXPORT PartSet_WidgetBSplinePoints : public ModuleBase_ModelWidget,
+                                                   public PartSet_MouseProcessor
+{
+Q_OBJECT
+public:
+  /// Constructor
+  /// \param theParent the parent object
+  /// \param theWorkshop a current workshop
+  /// \param theData the widget configuation. The attribute of the model widget is obtained from
+  PartSet_WidgetBSplinePoints(QWidget* theParent, ModuleBase_IWorkshop* theWorkshop,
+                              const Config_WidgetAPI* theData);
+  /// Destructor
+  virtual ~PartSet_WidgetBSplinePoints();
+
+  /// Fills given container with selection modes if the widget has it
+  /// \param [out] theModuleSelectionModes module additional modes, -1 means all default modes
+  /// \param theModes [out] a container of modes
+  virtual void selectionModes(int& theModuleSelectionModes, QIntList& theModes);
+
+  /// Checks if the selection presentation is valid in widget
+  /// \param theValue a selected presentation in the view
+  /// \return a boolean value
+  virtual bool isValidSelectionCustom(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue);
+
+  /// Checks all attribute validators returns valid. It tries on the given selection
+  /// to current attribute by setting the value inside and calling validators. After this,
+  /// the previous attribute value is restored.The valid/invalid value is cashed.
+  /// \param theValue a selected presentation in the view
+  /// \param theAttribute the attribute
+  /// \return a boolean value
+  bool isValidSelectionForAttribute_(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue,
+                                     const std::shared_ptr<ModelAPI_Attribute>& theAttribute);
+
+  /// Fills the attribute with the value of the selected owner
+  /// \param thePrs a selected owner
+  bool setSelectionCustom(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue);
+
+  /// Returns list of widget controls
+  /// \return a control list
+  virtual QList<QWidget*> getControls() const;
+
+  /// The methiod called when widget is deactivated
+  virtual void deactivate();
+
+  /// \returns the sketch instance
+  std::shared_ptr<ModelAPI_CompositeFeature> sketch() const { return mySketch; }
+
+  /// Set sketch instance
+  void setSketch(std::shared_ptr<ModelAPI_CompositeFeature> theSketch) { mySketch = theSketch; }
+
+  /// Fill the widget values by given point
+  /// \param theX the X coordinate
+  /// \param theY the Y coordinate
+  /// \returns True in case of success
+  bool setPoint(double theX, double theY);
+
+  /// Returns true if the event is processed.
+  virtual bool processEscape();
+
+  /// Returns true if the attribute can be changed using the selected shapes in the viewer
+  /// and creating a coincidence constraint to them. This control use them.
+  virtual bool useSelectedShapes() const;
+
+  /// Processing the mouse move event in the viewer
+  /// \param theWindow a view window
+  /// \param theEvent a mouse event
+  virtual void mouseMoved(ModuleBase_IViewWindow* theWindow, QMouseEvent* theEvent);
+
+  /// Processing the mouse release event in the viewer
+  /// \param theWindow a view window
+  /// \param theEvent a mouse event
+  virtual void mouseReleased(ModuleBase_IViewWindow* theWindow, QMouseEvent* theEvent);
+
+protected:
+  /// Saves the internal parameters to the given feature
+  /// \return True in success
+  virtual bool storeValueCustom();
+
+  /// Restore value from attribute data to the widget's control
+  virtual bool restoreValueCustom();
+
+  /// Store current value in cashed value
+  void storeCurentValue();
+
+  /// Restore cashed value in the model attribute
+  /// \return boolean state if the restored feature shoud be hidden
+  bool restoreCurentValue();
+
+  /// Fills the widget with default values
+  /// \return true if the widget current value is reset
+  virtual bool resetCustom();
+
+private:
+  /// Create labels for the next B-spline point
+  void createNextPoint();
+  /// Remove labels for the last B-spline point
+  void removeLastPoint();
+
+  /// Save B-spline poles and weights to corresponding attributes
+  void storePolesAndWeights() const;
+
+  /// Returns attribute reference if the key is defined in XML definition of this control
+  /// \return found attribute or null
+  std::shared_ptr<ModelAPI_AttributeRefAttrList> attributeRefAttrList() const;
+
+  void fillRefAttribute(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue);
+  void fillRefAttribute(std::shared_ptr<GeomAPI_Pnt2d> theClickedPoint,
+                        const std::shared_ptr<ModuleBase_ViewerPrs>& theValue);
+  void fillRefAttribute(const ObjectPtr& theObject);
+
+  ObjectPtr getGeomSelection(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue);
+
+protected:
+  ModuleBase_IWorkshop* myWorkshop; ///< workshop
+
+private:
+  QGroupBox* myBox;
+  QWidget* myGroupBox;  ///< the parent group box for all intenal widgets
+  QScrollArea* myScrollArea;
+  std::vector<ModuleBase_LabelValue*> myXSpin; ///< the label for the X coordinate
+  std::vector<ModuleBase_LabelValue*> myYSpin; ///< the label for the Y coordinate
+  PartSet_ExternalObjectsMgr* myExternalObjectMgr; ///< reference to external objects manager
+
+  /// value used as selection in mouse release method
+  std::shared_ptr<ModuleBase_ViewerPrs> myPreSelected;
+
+  /// it is important during restart operation
+  CompositeFeaturePtr mySketch;
+
+  std::string myRefAttribute; /// if not empty, coincidences are not set but attribute is filled
+
+  bool myValueIsCashed; /// boolean state if the value is cashed during value state change
+  bool myIsFeatureVisibleInCash; /// boolean value if the feature was visible when cash if filled
+  std::vector<double> myXValueInCash; /// the cashed X value during value state change
+  std::vector<double> myYValueInCash; /// the cashed Y value during value state change
+  std::vector<double> myWeightInCash; /// the cached Weight value during valude state change
+
+  std::string myWeightsAttr;
+
+  int myPointIndex; /// index of the changing point
+
+  bool myFinished; /// \c true if building the B-spline is finished (escape pressed)
+};
+
+#endif
index 7b5aff07db9500f3c91859e0433dc260a255222c..6cb8516c4431c010d5cd23739a2aa6a6bbdd96bc 100644 (file)
 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
 
+#include "PartSet_WidgetFeaturePointSelector.h"
+#include "PartSet_Tools.h"
+#include "PartSet_ExternalObjectsMgr.h"
+
 #include <Config_WidgetAPI.h>
 
 #include <Events_Loop.h>
@@ -40,9 +44,6 @@
 #include <ModuleBase_IWorkshop.h>
 #include <ModuleBase_IModule.h>
 
-#include "PartSet_WidgetFeaturePointSelector.h"
-#include "PartSet_Tools.h"
-
 #include <SketchPlugin_Point.h>
 
 #include <XGUI_Tools.h>
@@ -65,7 +66,8 @@ PartSet_WidgetFeaturePointSelector::PartSet_WidgetFeaturePointSelector(QWidget*
 : ModuleBase_WidgetShapeSelector(theParent, theWorkshop, theData)
 {
   std::string anAttributes = theData->getProperty("selection_attributes");
-  QStringList anAttributesList = QString(anAttributes.c_str()).split(' ', QString::SkipEmptyParts);
+  QStringList anAttributesList =
+    QString(anAttributes.c_str()).split(' ', QString::SkipEmptyParts);
 
   myHasPreview = anAttributesList.size() >= 4;
 
@@ -75,10 +77,13 @@ PartSet_WidgetFeaturePointSelector::PartSet_WidgetFeaturePointSelector(QWidget*
     myPreviewObjectAttribute = anAttributesList[2].toStdString();
     myPreviewPointAttribute = anAttributesList[3].toStdString();
   }
+  myExternalObjectMgr = new PartSet_ExternalObjectsMgr(theData->getProperty("use_external"),
+    theData->getProperty("can_create_external"), true);
 }
 
 PartSet_WidgetFeaturePointSelector::~PartSet_WidgetFeaturePointSelector()
 {
+  delete myExternalObjectMgr;
 }
 
 //********************************************************************
@@ -89,37 +94,6 @@ bool PartSet_WidgetFeaturePointSelector::isValidSelection(
   //return true;
 }
 
-//********************************************************************
-void PartSet_WidgetFeaturePointSelector::updateSelectionModesAndFilters(bool toActivate)
-{
-#ifdef HIGHLIGHT_STAYS_PROBLEM
-  Handle(AIS_InteractiveContext) aContext =
-                            XGUI_Tools::workshop(myWorkshop)->viewer()->AISContext();
-  Quantity_Color aColor;
-  Handle(Prs3d_Drawer) aHStyle = aContext->HighlightStyle();
-  Handle(Prs3d_Drawer) aSStyle = aContext->SelectionStyle();
-  if (toActivate) {
-    std::vector<int> aColors;
-    aColors = Config_PropManager::color("Visualization", "sketch_entity_color");
-    aColor = Quantity_Color(aColors[0] / 255., aColors[1] / 255., aColors[2] / 255.,
-                            Quantity_TOC_RGB);
-    myHighlightColor = aHStyle->Color();
-    mySelectionColor = aSStyle->Color();
-  }
-  else {
-    aColor = myHighlightColor;
-  }
-  aHStyle->SetColor(aColor);
-  aContext->SetHighlightStyle(aHStyle);
-
-  aSStyle->SetColor(aColor);
-  aContext->SetSelectionStyle(aSStyle);
-
-#endif
-
-  ModuleBase_WidgetShapeSelector::updateSelectionModesAndFilters(toActivate);
-}
-
 //********************************************************************
 void PartSet_WidgetFeaturePointSelector::activateCustom()
 {
@@ -144,7 +118,16 @@ void PartSet_WidgetFeaturePointSelector::mouseMoved(ModuleBase_IViewWindow* theW
 
   ModuleBase_ViewerPrsPtr aPrs = !aHighlighted.empty() ? aHighlighted.first()
                                                        : ModuleBase_ViewerPrsPtr();
-  fillFeature(aPrs, theWindow, theEvent);
+  myPreviewPoint = PartSet_Tools::getPnt2d(theEvent, theWindow, mySketch);
+  if (myHasPreview) {
+    if (aPrs.get() && aPrs->object().get())
+      myPreviewObject = aPrs->object();
+    else
+      myPreviewObject = ObjectPtr();
+    fillFeature();
+    updateObject(feature());
+    Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
+  }
 }
 
 //********************************************************************
@@ -155,6 +138,20 @@ void PartSet_WidgetFeaturePointSelector::mouseReleased(ModuleBase_IViewWindow* t
   if (theEvent->button() != Qt::LeftButton)
     return;
 
+  ModuleBase_ISelection* aSelection = myWorkshop->selection();
+  QList<ModuleBase_ViewerPrsPtr> aSelected =
+    aSelection->getSelected(ModuleBase_ISelection::Viewer);
+
+  ModuleBase_ViewerPrsPtr aPrs =
+    !aSelected.empty() ? aSelected.first() : ModuleBase_ViewerPrsPtr();
+  if (aPrs.get() && aPrs->object().get()) {
+    myPreviewObject = aSelection->getResult(aPrs);
+    GeomShapePtr aShape = aSelection->getShape(aPrs);
+    myExternalObjectMgr->getGeomSelection(aPrs, myPreviewObject, aShape,
+      myWorkshop, sketch(), true);
+  }
+  myPreviewPoint = PartSet_Tools::getPnt2d(theEvent, theWindow, mySketch);
+
   ObjectPtr aPreviewObject;
   GeomPnt2dPtr aPreviewPoint;
   if (myHasPreview) {
@@ -177,6 +174,10 @@ void PartSet_WidgetFeaturePointSelector::mouseReleased(ModuleBase_IViewWindow* t
   if (!aPreviewObject.get())
     return;
 
+  // Do not use non-sketcher objects
+  if (!sketch()->isSub(aPreviewObject))
+    return;
+
   // set parameters of preview into parameters of selection in the feature
   std::shared_ptr<GeomDataAPI_Point2D> aPointSelectedAttr =
                           std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
@@ -203,38 +204,47 @@ void PartSet_WidgetFeaturePointSelector::mouseReleased(ModuleBase_IViewWindow* t
 }
 
 //********************************************************************
-bool PartSet_WidgetFeaturePointSelector::fillFeature(
-                            const std::shared_ptr<ModuleBase_ViewerPrs>& theSelectedPrs,
-                            ModuleBase_IViewWindow* theWindow,
-                            QMouseEvent* theEvent)
+bool PartSet_WidgetFeaturePointSelector::fillFeature()
 {
-  bool aFilled = false;
-  if (theSelectedPrs.get() && theSelectedPrs->object().get())
-    myPreviewObject = theSelectedPrs->object();
-  myPreviewPoint = PartSet_Tools::getPnt2d(theEvent, theWindow, mySketch);
-
   if (myHasPreview) {
     std::shared_ptr<ModelAPI_AttributeReference> aRef =
                             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(
                             feature()->data()->attribute(myPreviewObjectAttribute));
     aRef->setValue(myPreviewObject);
-
-    std::shared_ptr<GeomDataAPI_Point2D> anAttributePoint =
-                    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-                    feature()->data()->attribute(myPreviewPointAttribute));
-    anAttributePoint->setValue(myPreviewPoint);
+    if (myPreviewPoint.get()) {
+      std::shared_ptr<GeomDataAPI_Point2D> anAttributePoint =
+        std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+          feature()->data()->attribute(myPreviewPointAttribute));
+      anAttributePoint->setValue(myPreviewPoint);
+    }
+  }
+  else {
+    // Do not use non-sketcher objects
+    if (!sketch()->isSub(myPreviewObject))
+      return false;
+
+    // set parameters of preview into parameters of selection in the feature
+    if (myPreviewPoint.get()) {
+      std::shared_ptr<GeomDataAPI_Point2D> aPointSelectedAttr =
+        std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+          feature()->data()->attribute(mySelectedPointAttribute));
+      aPointSelectedAttr->setValue(myPreviewPoint);
+    }
+    AttributeReferencePtr aRefSelectedAttr = feature()->reference(mySelectedObjectAttribute);
+    if (aRefSelectedAttr)
+      aRefSelectedAttr->setValue(myPreviewObject);
+    else {
+      AttributeRefAttrPtr aRefAttrSelectedAttr = feature()->refattr(mySelectedObjectAttribute);
+      if (aRefAttrSelectedAttr)
+        aRefAttrSelectedAttr->setObject(myPreviewObject);
+    }
   }
   // redisplay AIS presentation in viewer
 #ifndef HIGHLIGHT_STAYS_PROBLEM
   // an attempt to clear highlighted item in the viewer: but of OCCT
   XGUI_Tools::workshop(myWorkshop)->displayer()->clearSelected(true);
 #endif
-  updateObject(feature());
-  Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
-
-  aFilled = true;
-
-  return aFilled;
+  return true;
 }
 
 //********************************************************************
@@ -244,16 +254,22 @@ QList<ModuleBase_ViewerPrsPtr> PartSet_WidgetFeaturePointSelector::getAttributeS
 }
 
 //********************************************************************
-bool PartSet_WidgetFeaturePointSelector::setSelection(
-                                          QList<std::shared_ptr<ModuleBase_ViewerPrs>>& theValues,
-                                          const bool theToValidate)
+bool PartSet_WidgetFeaturePointSelector::setSelectionCustom(const ModuleBase_ViewerPrsPtr& thePrs)
 {
-  // false is returned to do not emit focus out widget by selected sub-shape
-  return false;
+  if (!thePrs.get() || !thePrs->object().get())
+    return false;
+
+  ModuleBase_ISelection* aSelection = myWorkshop->selection();
+  myPreviewObject = aSelection->getResult(thePrs);
+  GeomShapePtr aShape = aSelection->getShape(thePrs);
+  myExternalObjectMgr->getGeomSelection(thePrs, myPreviewObject, aShape, myWorkshop,
+    sketch(), true);
+  return fillFeature();
 }
 
+//********************************************************************
 void PartSet_WidgetFeaturePointSelector::setPreSelection(
-                                  const std::shared_ptr<ModuleBase_ViewerPrs>& thePreSelected,
+                                  const ModuleBase_ViewerPrsPtr& thePreSelected,
                                   ModuleBase_IViewWindow* theWnd,
                                   QMouseEvent* theEvent)
 {
@@ -261,3 +277,23 @@ void PartSet_WidgetFeaturePointSelector::setPreSelection(
   // sub-segments in the viewer, secondly preselection of restart operation is processed by
   // special reentrant message sent by the feature
 }
+
+//********************************************************************
+void PartSet_WidgetFeaturePointSelector::getGeomSelection(const ModuleBase_ViewerPrsPtr& thePrs,
+  ObjectPtr& theObject, GeomShapePtr& theShape)
+{
+  ModuleBase_WidgetShapeSelector::getGeomSelection(thePrs, theObject, theShape);
+
+  myExternalObjectMgr->getGeomSelection(thePrs, theObject, theShape,
+    myWorkshop, sketch(), myIsInValidate);
+  myPreviewObject = theObject;
+}
+
+//********************************************************************
+void PartSet_WidgetFeaturePointSelector::restoreAttributeValue(const AttributePtr& theAttribute,
+  const bool theValid)
+{
+  ModuleBase_WidgetShapeSelector::restoreAttributeValue(theAttribute, theValid);
+  myExternalObjectMgr->removeExternal(sketch(), myFeature, myWorkshop, true);
+  myPreviewObject = ObjectPtr();
+}
index 972b30ede1e581a4ac69090b2e3aa3a25fd2fe83..36c6878f7b60e17701e089315ea3b6deea9909a0 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <ModelAPI_CompositeFeature.h>
 #include <ModuleBase_WidgetShapeSelector.h>
+#include <ModuleBase_ViewerPrs.h>
 
 #include "PartSet.h"
 #include "PartSet_MouseProcessor.h"
@@ -36,6 +37,7 @@ class ModuleBase_IWorkshop;
 class Config_WidgetAPI;
 class ModuleBase_IViewWindow;
 class ModuleBase_ViewerPrs;
+class PartSet_ExternalObjectsMgr;
 
 class GeomAPI_Pnt;
 class GeomAPI_Pnt2d;
@@ -72,10 +74,6 @@ Q_OBJECT
   /// \return a boolean value
   virtual bool isValidSelection(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue);
 
-  /// Activate or deactivate selection and selection filters
-  /// \return true if the selection filter of the widget is activated in viewer context
-  virtual void updateSelectionModesAndFilters(bool toActivate);
-
   /// Set sketcher
   /// \param theSketch a sketcher object
   void setSketcher(CompositeFeaturePtr theSketch) { mySketch = theSketch; }
@@ -96,17 +94,13 @@ Q_OBJECT
   /// \param theEvent a mouse event
   virtual void mouseReleased(ModuleBase_IViewWindow* theWindow, QMouseEvent* theEvent);
 
-  /// Set the given wrapped value to the current widget
-  /// This value should be processed in the widget according to the needs
-  /// The method is called by the current operation to process the operation preselection.
-  /// It is redefined to fill attributes responsible for the sub selection
-  /// \param theValues the wrapped selection values
-  /// \param theToValidate a flag on validation of the values
-  virtual bool setSelection(QList<std::shared_ptr<ModuleBase_ViewerPrs>>& theValues,
-                            const bool theToValidate);
+
+  /// Fills the attribute with the value of the selected owner
+  /// \param thePrs a selected owner
+  virtual bool setSelectionCustom(const ModuleBase_ViewerPrsPtr& thePrs);
 
   /// Fill preselection used in mouseReleased
-  virtual void setPreSelection(const std::shared_ptr<ModuleBase_ViewerPrs>& thePreSelected,
+  virtual void setPreSelection(const ModuleBase_ViewerPrsPtr& thePreSelected,
                                ModuleBase_IViewWindow* theWnd,
                                QMouseEvent* theEvent);
 protected:
@@ -115,14 +109,25 @@ protected:
   /// a shape. If the attribute do not uses the shape, it is empty
   virtual QList<std::shared_ptr<ModuleBase_ViewerPrs>> getAttributeSelection() const;
 
-protected:
   /// The methiod called when widget is activated
   virtual void activateCustom();
 
+  /// Return an object and geom shape by the viewer presentation
+  /// \param thePrs a selection
+  /// \param theObject an output object
+  /// \param theShape a shape of the selection
+  virtual void getGeomSelection(const std::shared_ptr<ModuleBase_ViewerPrs>& thePrs,
+    ObjectPtr& theObject, GeomShapePtr& theShape);
+
+  /// Creates a backup of the current values of the attribute
+  /// It should be realized in the specific widget because of different
+  /// parameters of the current attribute
+  /// \param theAttribute an attribute
+  /// \param theValid a boolean flag, if restore happens for valid parameters
+  void restoreAttributeValue(const AttributePtr& theAttribute, const bool theValid);
+
 protected:
-  bool fillFeature(const std::shared_ptr<ModuleBase_ViewerPrs>& theSelectedPrs,
-                   ModuleBase_IViewWindow* theWnd,
-                   QMouseEvent* theEvent);
+  bool fillFeature();
 
   /// Pointer to a sketch
   CompositeFeaturePtr mySketch;
@@ -137,6 +142,7 @@ protected:
   bool myHasPreview;
   std::shared_ptr<ModelAPI_Object> myPreviewObject;
   std::shared_ptr<GeomAPI_Pnt2d>   myPreviewPoint;
+  PartSet_ExternalObjectsMgr* myExternalObjectMgr; ///< reference to external objects manager
 };
 
 #endif
\ No newline at end of file
index 8183806b9d66b41da4ca2e03093a3734821a68f8..0574f2e0003c37ab6bdbb81eff764a6af4d1bb77 100644 (file)
@@ -243,10 +243,10 @@ bool PartSet_WidgetPoint2D::setSelectionCustom(const ModuleBase_ViewerPrsPtr& th
   GeomShapePtr aShape = theValue->shape();
   if (aShape.get() && !aShape->isNull()) {
     Handle(V3d_View) aView = myWorkshop->viewer()->activeView();
-    double aX = 0, aY = 0;
     const TopoDS_Shape& aTDShape = aShape->impl<TopoDS_Shape>();
-    if (getPoint2d(aView, aTDShape, aX, aY)) {
-      fillRefAttribute(aX, aY, theValue);
+    GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aTDShape, mySketch);
+    if (aPnt) {
+      fillRefAttribute(aPnt->x(), aPnt->y(), theValue);
       isDone = true;
     }
     else if (aTDShape.ShapeType() == TopAbs_EDGE) {
@@ -306,11 +306,11 @@ bool PartSet_WidgetPoint2D::setSelection(QList<ModuleBase_ViewerPrsPtr>& theValu
     GeomShapePtr aShape = aValue->shape();
     if (aShape.get() && !aShape->isNull()) {
       Handle(V3d_View) aView = myWorkshop->viewer()->activeView();
-      double aX = 0, aY = 0;
       const TopoDS_Shape& aTDShape = aShape->impl<TopoDS_Shape>();
-      if (getPoint2d(aView, aTDShape, aX, aY)) {
-        isDone = setPoint(aX, aY);
-        setConstraintToPoint(aX, aY, aValue);
+      GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aTDShape, mySketch);
+      if (aPnt) {
+        isDone = setPoint(aPnt->x(), aPnt->y());
+        setConstraintToPoint(aPnt->x(), aPnt->y(), aValue);
       }
     }
   }
@@ -476,24 +476,6 @@ void PartSet_WidgetPoint2D::deactivate()
   ModuleBase_ModelWidget::deactivate();
 }
 
-bool PartSet_WidgetPoint2D::getPoint2d(const Handle(V3d_View)& theView,
-                                       const TopoDS_Shape& theShape,
-                                       double& theX, double& theY) const
-{
-  if (!theShape.IsNull()) {
-    if (theShape.ShapeType() == TopAbs_VERTEX) {
-      const TopoDS_Vertex& aVertex = TopoDS::Vertex(theShape);
-      if (!aVertex.IsNull()) {
-        // A case when point is taken from existing vertex
-        gp_Pnt aPoint = BRep_Tool::Pnt(aVertex);
-        PartSet_Tools::convertTo2D(aPoint, mySketch, theView, theX, theY);
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
 bool PartSet_WidgetPoint2D::setConstraintToPoint(double theClickedX, double theClickedY,
                                   const std::shared_ptr<ModuleBase_ViewerPrs>& theValue)
 {
@@ -627,14 +609,19 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo
       }
     }
     if (anExternal) {
+      GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aShape, mySketch);
       double aX = 0, aY = 0;
-      if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) {
+      if (aPnt) {
+        aX = aPnt->x();
+        aY = aPnt->y();
+      }
+      if (aPnt && isFeatureContainsPoint(myFeature, aX, aY)) {
         // do not create a constraint to the point, which already used by the feature
         // if the feature contains the point, focus is not switched
         setPoint(aX, aY);
       }
       else {
-        if (getPoint2d(aView, aShape, aX, aY))
+        if (aPnt)
           setPoint(aX, aY);
         else {
           if (aShape.ShapeType() == TopAbs_EDGE) {
@@ -670,24 +657,28 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo
         if (!anOrphanPoint)
           emit vertexSelected(); // it stops the reentrant operation
 
+        myPreSelected.reset();
         emit focusOutWidget(this);
       }
     }
     if (!anExternal) {
-      double aX = 0, aY = 0;
       bool isProcessed = false;
-      if (getPoint2d(aView, aShape, aX, aY) && isFeatureContainsPoint(myFeature, aX, aY)) {
+      GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aShape, mySketch);
+      if (aPnt && isFeatureContainsPoint(myFeature, aPnt->x(), aPnt->y())) {
         // when the point is selected, the coordinates of the point should be set into the attribute
         // if the feature contains the point, focus is not switched
-        setPoint(aX, aY);
+        setPoint(aPnt->x(), aPnt->y());
       }
       else {
+        double aX = 0, aY = 0;
         bool anOrphanPoint = isOrphanPoint(aSelectedFeature, mySketch, aX, aY);
         // do not set a coincidence constraint in the attribute if the feature contains a point
         // with the same coordinates. It is important for line creation in order to do not set
         // the same constraints for the same points, oterwise the result line has zero length.
         bool isAuxiliaryFeature = false;
-        if (getPoint2d(aView, aShape, aX, aY)) {
+        if (aPnt) {
+          aX = aPnt->x();
+          aY = aPnt->y();
           setPoint(aX, aY);
           setConstraintToPoint(aX, aY, aFirstValue);
         }
@@ -711,6 +702,7 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo
         updateObject(feature());
         if (!anOrphanPoint && !anExternal && !isAuxiliaryFeature)
           emit vertexSelected();
+        myPreSelected.reset();
         emit focusOutWidget(this);
       }
     }
@@ -738,13 +730,14 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo
       // external objects e.g. selection of trihedron axis when input end arc point
       updateObject(feature());
 
-      double aX = 0, aY = 0;
-      if (getPoint2d(aView, aShape, aX, aY)) {
+      GeomPnt2dPtr aPnt = PartSet_Tools::getPnt2d(aView, aShape, mySketch);
+      if (aPnt) {
         // do not create a constraint to the point, which already used by the feature
         // if the feature contains the point, focus is not switched
-        setPoint(aX, aY);
+        setPoint(aPnt->x(), aPnt->y());
       }
       emit vertexSelected(); // it stops the reentrant operation
+      myPreSelected.reset();
       emit focusOutWidget(this);
     }
   }
@@ -758,6 +751,7 @@ void PartSet_WidgetPoint2D::mouseReleased(ModuleBase_IViewWindow* theWindow, QMo
     if (!setPoint(aX, aY) || isFeatureContainsPoint(myFeature, aX, aY))
       return;
 
+    myPreSelected.reset();
     emit focusOutWidget(this);
   }
 }
@@ -769,7 +763,6 @@ void PartSet_WidgetPoint2D::setPreSelection(
 {
   myPreSelected = thePreSelected;
   mouseReleased(theWnd, theEvent);
-  myPreSelected = ModuleBase_ViewerPrsPtr();
 }
 
 void PartSet_WidgetPoint2D::getGeomSelection_(const std::shared_ptr<ModuleBase_ViewerPrs>& theValue,
index 515a03c4120a7856bf950eee829b15285e887757..3a062cfa486b5fddc1e4ebdba079ab31cf76561e 100644 (file)
@@ -211,14 +211,6 @@ protected:
   virtual void initializeValueByActivate();
 
  private:
-   /// Returns point 2d from selected vertex
-   /// \param theView a view window
-   /// \param theShape a vertex shape
-   /// \param theX an output value of X coordinate
-   /// \param theY an output value of Y coordinate
-   bool getPoint2d(const Handle(V3d_View)& theView, const TopoDS_Shape& theShape,
-                   double& theX, double& theY) const;
-
    /// Creates constrains of the clicked point
    /// \param theClickedX the horizontal coordnate of the point
    /// \param theClickedY the vertical coordnate of the point
@@ -230,6 +222,7 @@ protected:
    /// \return true if succed
    bool setConstraintToObject(const ObjectPtr& theObject);
 
+public:
    /// Returns if the feature is an orphan point, circle or an arc. Returns true if it
    /// has no a coincident to other lines. It processes point, circle and arc features
    /// In circle an arc features, only centers are processed, for other points, it returns
@@ -274,6 +267,7 @@ protected:
                                        const FeaturePtr& theSkipFeature,
                                        const std::shared_ptr<GeomAPI_Pnt2d>& thePoint);
 
+private:
   /// Returns attribute reference if the key is defined in XML definition of this control
   /// \return found attribute or null
   std::shared_ptr<ModelAPI_AttributeRefAttr> attributeRefAttr() const;
index 0a14abf97dc356d03dd68d6a76c81ebb34ad22d4..577689a6cca2ca6e5fe8ea304b8f510f773d30f4 100644 (file)
@@ -451,6 +451,15 @@ void PartSet_WidgetSketchLabel::restoreAttributeValue(const AttributePtr& theAtt
     GeomShapePtr anEmptyShape;
     aSelAttr->setValue(anEmptyResult, anEmptyShape);
   }
+  std::shared_ptr<GeomDataAPI_Point> anOrigin = std::dynamic_pointer_cast<GeomDataAPI_Point>(
+    aData->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
+  anOrigin->reset();
+  std::shared_ptr<GeomDataAPI_Dir> aNormal = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+    aData->attribute(SketchPlugin_Sketch::NORM_ID()));
+  aNormal->reset();
+  std::shared_ptr<GeomDataAPI_Dir> aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
+    aData->attribute(SketchPlugin_Sketch::DIRX_ID()));
+  aDirX->reset();
 }
 
 bool PartSet_WidgetSketchLabel::setSelectionCustom(const ModuleBase_ViewerPrsPtr& thePrs)
@@ -586,16 +595,19 @@ void PartSet_WidgetSketchLabel::showEvent(QShowEvent* theEvent)
 
 void PartSet_WidgetSketchLabel::onShowPanel()
 {
-  if (mySizeOfViewWidget->isVisible()) {
+  //if (mySizeOfViewWidget->isVisible()) {
+  if (myStackWidget->currentIndex() == 0) {
     DocumentPtr aDoc = feature()->document();
     DocumentPtr aModDoc = ModelAPI_Session::get()->moduleDocument();
     if (aModDoc == aDoc) {
       myPartSetMessage->move(mapToGlobal(geometry().bottomLeft()));
       myPartSetMessage->show();
     }
-    QPoint aPnt = mySizeOfView->mapToGlobal(mySizeOfView->geometry().center());
-    mySizeMessage->move(aPnt);
-    mySizeMessage->show();
+    if (mySizeOfViewWidget->isVisible()) {
+      QPoint aPnt = mySizeOfView->mapToGlobal(mySizeOfView->geometry().center());
+      mySizeMessage->move(aPnt);
+      mySizeMessage->show();
+    }
   }
 }
 
index 2bb1a66e2c583c4315a7b00a71495d79b52aefca..ad535366f9bfd4d57149d1ecbc3a93aca311e5b0 100644 (file)
@@ -1,6 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE TS>
 <TS version="2.1" language="fr_FR">
+<context>
+    <name>PartSet_BSplineWidget</name>
+    <message>
+        <source>Poles and weights</source>
+        <translation>Poteaux et poids</translation>
+    </message>
+    <message>
+        <source>B-spline poles and weights</source>
+        <translation type="vanished">Poteaux et poids B-spline</translation>
+    </message>
+    <message>
+        <source>Pole %1</source>
+        <translation>Pôle %1</translation>
+    </message>
+    <message>
+        <source>Weight :</source>
+        <translation>Poids :</translation>
+    </message>
+    <message>
+        <source>Add a new pole after the current</source>
+        <translation>Ajouter un nouveau pôle après le courant</translation>
+    </message>
+    <message>
+        <source>X</source>
+        <translation>X</translation>
+    </message>
+    <message>
+        <source>Y</source>
+        <translation>Y</translation>
+    </message>
+</context>
 <context>
     <name>PartSet_MenuMgr</name>
     <message>
         <source>Detach %1</source>
         <translation>Détachez %1</translation>
     </message>
+    <message>
+        <source>Load all parts</source>
+        <translation>Charger toutes les parties</translation>
+    </message>
+</context>
+<context>
+    <name>PartSet_WidgetBSplinePoints</name>
+    <message>
+        <source>Pole %1</source>
+        <translation>Pôle %1</translation>
+    </message>
+    <message>
+        <source>Create control polygon</source>
+        <translation type="vanished"> Créer un polygone de contrôle</translation>
+    </message>
+    <message>
+        <source>Specify if the control polygon should be created</source>
+        <translation type="vanished">Spécifiez si le polygone de contrôle doit être créé</translation>
+    </message>
+    <message>
+        <source>X</source>
+        <translation>X</translation>
+    </message>
+    <message>
+        <source>Y</source>
+        <translation>Y</translation>
+    </message>
 </context>
 <context>
     <name>PartSet_WidgetPoint2D</name>
@@ -112,7 +170,17 @@ Erreur : %1</translation>
     </message>
     <message>
         <source>Show remaining DoFs</source>
-        <translation type="unfinished"></translation>
+        <translation>Afficher le reste DoFs</translation>
+    </message>
+    <message>
+        <source>The Sketch is created in PartSet.
+It will be necessary to create a Part in order to use this sketch for body creation</source>
+        <translation>L&apos;esquisse est créée dans PartSet.
+Il sera nécessaire de créer une pièce afin d&apos;utiliser cette esquisse pour la création du corps</translation>
+    </message>
+    <message>
+        <source>A size of Sketch view can be defined here.</source>
+        <translation>Une taille de vue Esquisse peut être définie ici.</translation>
     </message>
 </context>
 <context>
index 475fdbae834ef5d578a50c2407ce91628d8e4fa5..ca5f25fecf83bd41ff08317a39ba251d7d912f12 100644 (file)
@@ -31,3 +31,5 @@ from FeaturesAPI import addFillet, addChamfer
 from FeaturesAPI import addFusionFaces
 from FeaturesAPI import measureLength, measureDistance, measureRadius, measureAngle
 from FeaturesAPI import addRemoveResults
+from FeaturesAPI import addCopy, addImportResult
+from FeaturesAPI import addDefeaturing
index 117e561d2bb0b258f9f5e14b57d583659d966f25..1ac461065e957c789673cdf65b4cd0e760221c6e 100644 (file)
@@ -74,7 +74,9 @@
 #include <QMenu>
 #include <QToolBar>
 
-#define SALOME_PATCH_FOR_CTRL_WHEEL
+#if OCC_VERSION_HEX < 0x070400
+  #define SALOME_PATCH_FOR_CTRL_WHEEL
+#endif
 
 extern "C" {
 SHAPERGUI_EXPORT CAM_Module* createModule()
@@ -168,7 +170,7 @@ void SHAPERGUI::initialize(CAM_Application* theApp)
   }
 
   int aMenu = createMenu(tr("Inspection"), -1, -1, 30);
-  int aSubMenu = createMenu(tr("Information"), aMenu);
+  int aSubMenu = createMenu(tr("Information"), aMenu, -1, -1, 0);
 
   int aId = getNextCommandId();
   myActionsList.append(aId);
@@ -180,7 +182,7 @@ void SHAPERGUI::initialize(CAM_Application* theApp)
   myWhatIsAction->setData("INSPECTION_CMD");
   createMenu(aId, aSubMenu, 0);
 
-  QString aToolName = tr("Inspection tool");
+  QString aToolName = tr("Inspection");
   int aTool = createTool(aToolName);
   int aToolId = createTool(myWhatIsAction, aTool);
   registerCommandToolbar(aToolName, aId);
index 1f0d054f491c861650d04b4d833b68c8a6739529..12776d98c79d7330680358749e708f6daa7be148 100644 (file)
@@ -33,7 +33,9 @@
 #include <QMouseEvent>
 #include <QContextMenuEvent>
 
-#define SALOME_PATCH_FOR_CTRL_WHEEL
+#if OCC_VERSION_HEX < 0x070400
+  #define SALOME_PATCH_FOR_CTRL_WHEEL
+#endif
 
 SHAPERGUI_SalomeView::SHAPERGUI_SalomeView(OCCViewer_Viewer* theViewer)
 : ModuleBase_IViewWindow(), myCurrentView(0)
@@ -405,7 +407,7 @@ void SHAPERGUI_SalomeViewer::setViewProjection(double theX, double theY,
       aView3d->SetProj(theX, theY, theZ);
       aView3d->SetTwist( theTwist );
       aView3d->FitAll(0.01, false);
-      aView3d->SetZSize(0.);
+      //aView3d->SetZSize(0.);
       if (aView3d->Depth() < 0.1)
         aView3d->DepthFitAll();
     }
index 99bf031493b240003f53c37be88428c27dab27e0..c0c4f42529099dd51ca8d5ea41736129ee0f9f48 100644 (file)
@@ -21,7 +21,7 @@
     </message>
     <message>
         <source>Inspection tool</source>
-        <translation>Outil d&apos;inspection</translation>
+        <translation type="vanished">Outil d&apos;inspection</translation>
     </message>
     <message>
         <source>Edit toolbars of the module</source>
     </message>
     <message>
         <source> (%1 commands)</source>
-        <translation type="unfinished"></translation>
+        <translation> (%1 commandes)</translation>
     </message>
 </context>
 </TS>
index 246e0cdeca59ad19233480aa70bef6a95854878e..c73762a7d96a55e8d8a883336557c9b41e490790 100644 (file)
@@ -48,32 +48,34 @@ static void findBases(TDF_Label theAccess, Handle(TNaming_NamedShape) theFinal,
   bool aMustBeAtFinal, const TDF_Label& theAdditionalDocument, TDF_LabelList& theResult)
 {
   bool aFoundAnyShape = false;
-  TNaming_SameShapeIterator aLabIter(theValue, theAccess);
-  for(; aLabIter.More(); aLabIter.Next()) {
-    Handle(TNaming_NamedShape) aNS;
-    if (aLabIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
-      if (aMustBeAtFinal && aNS != theFinal)
-        continue; // looking for old at the same final label only
-      TNaming_Evolution anEvolution = aNS->Evolution();
-      if (anEvolution == TNaming_PRIMITIVE) {
-        // check that this is not in the results already
-        const TDF_Label aResult = aNS->Label();
-        TDF_LabelList::Iterator aResIter(theResult);
-        for(; aResIter.More(); aResIter.Next()) {
-          if (aResIter.Value().IsEqual(aResult))
-            break;
+  if (TNaming_Tool::HasLabel(theAccess, theValue)) {
+    TNaming_SameShapeIterator aLabIter(theValue, theAccess);
+    for(; aLabIter.More(); aLabIter.Next()) {
+      Handle(TNaming_NamedShape) aNS;
+      if (aLabIter.Label().FindAttribute(TNaming_NamedShape::GetID(), aNS)) {
+        if (aMustBeAtFinal && aNS != theFinal)
+          continue; // looking for old at the same final label only
+        TNaming_Evolution anEvolution = aNS->Evolution();
+        if (anEvolution == TNaming_PRIMITIVE) {
+          // check that this is not in the results already
+          const TDF_Label aResult = aNS->Label();
+          TDF_LabelList::Iterator aResIter(theResult);
+          for(; aResIter.More(); aResIter.Next()) {
+            if (aResIter.Value().IsEqual(aResult))
+              break;
+          }
+          if (!aResIter.More()) // not found, so add this new
+            theResult.Append(aResult);
+          aFoundAnyShape = true;
         }
-        if (!aResIter.More()) // not found, so add this new
-          theResult.Append(aResult);
-        aFoundAnyShape = true;
-      }
-      if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
-        for(TNaming_Iterator aThisIter(aNS); aThisIter.More(); aThisIter.Next()) {
-          if (aThisIter.NewShape().IsSame(theValue)) {
-            // continue recursively, null NS means that any NS are ok
-            findBases(theAccess, theFinal, aThisIter.OldShape(),
-              false, theAdditionalDocument, theResult);
-            aFoundAnyShape = true;
+        if (anEvolution == TNaming_GENERATED || anEvolution == TNaming_MODIFY) {
+          for(TNaming_Iterator aThisIter(aNS); aThisIter.More(); aThisIter.Next()) {
+            if (aThisIter.NewShape().IsSame(theValue)) {
+              // continue recursively, null NS means that any NS are ok
+              findBases(theAccess, theFinal, aThisIter.OldShape(),
+                false, theAdditionalDocument, theResult);
+              aFoundAnyShape = true;
+            }
           }
         }
       }
index 9d2b423d8a244dc27cab1286d334d11b2ec35c43..c81d857be876744e1ae073b9f30c09cd77b8b3f7 100644 (file)
@@ -22,6 +22,7 @@ INCLUDE(Common)
 SET(PROJECT_HEADERS
   SketchAPI.h
   SketchAPI_Arc.h
+  SketchAPI_BSpline.h
   SketchAPI_Circle.h
   SketchAPI_Constraint.h
   SketchAPI_ConstraintAngle.h
@@ -45,6 +46,7 @@ SET(PROJECT_HEADERS
 
 SET(PROJECT_SOURCES
   SketchAPI_Arc.cpp
+  SketchAPI_BSpline.cpp
   SketchAPI_Circle.cpp
   SketchAPI_Constraint.cpp
   SketchAPI_ConstraintAngle.cpp
index 08f87c104fe754f46b9de4453fa37d5094c9f3a3..6d113b794dc297e1eb211a20b5d2f023127bbe7e 100644 (file)
@@ -32,6 +32,7 @@
 %include "doxyhelp.i"
 
 // import other modules
+%import "GeomAPI.i"
 %import "ModelAPI.i"
 %import "ModelHighAPI.i"
 
 %include "std_shared_ptr.i"
 
 // function with named parameters
+%feature("kwargs") SketchAPI_BSpline::controlPoles;
+%feature("kwargs") SketchAPI_BSpline::controlPolygon;
 %feature("kwargs") SketchAPI_Ellipse::construction;
 %feature("kwargs") SketchAPI_EllipticArc::construction;
+%feature("kwargs") SketchAPI_Sketch::addSpline;
+%feature("kwargs") SketchAPI_Sketch::setAngle;
 
 // shared pointers
 %shared_ptr(SketchAPI_Arc)
@@ -57,6 +62,8 @@
 %shared_ptr(SketchAPI_MacroEllipse)
 %shared_ptr(SketchAPI_EllipticArc)
 %shared_ptr(SketchAPI_MacroEllipticArc)
+%shared_ptr(SketchAPI_BSpline)
+%shared_ptr(SketchAPI_BSplinePeriodic)
 %shared_ptr(SketchAPI_Constraint)
 %shared_ptr(SketchAPI_ConstraintAngle)
 %shared_ptr(SketchAPI_IntersectionPoint)
@@ -74,6 +81,7 @@
 %template(InterfaceList) std::list<std::shared_ptr<ModelHighAPI_Interface> >;
 %template(EntityList)    std::list<std::shared_ptr<SketchAPI_SketchEntity> >;
 %template(SketchPointList) std::list<std::shared_ptr<SketchAPI_Point> >;
+%template(GeomPnt2dList) std::list<std::shared_ptr<GeomAPI_Pnt2d> >;
 // std::pair -> []
 %template(PointRefAttrPair) std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>;
 
 // fix compilarion error: 'res*' was not declared in this scope
 %typemap(freearg) const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> & {}
 
+
+%typemap(in) const std::list<std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> > & (std::list<std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> > temp) {
+  if (PySequence_Check($input)) {
+    for (Py_ssize_t i = 0; i < PySequence_Size($input); ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+
+      std::list<PyObject*> temp_inputlist;
+      if (PySequence_Check(item)) {
+        for (Py_ssize_t i = 0; i < PySequence_Size(item); ++i) {
+          PyObject * tmpItem = PySequence_GetItem(item, i);
+          temp_inputlist.push_back(tmpItem);
+        }
+      } else {
+        temp_inputlist.push_back(item);
+      }
+
+      std::shared_ptr<ModelAPI_Attribute> * temp_attribute = 0;
+      std::shared_ptr<ModelAPI_Object> * temp_object = 0;
+      std::shared_ptr<ModelHighAPI_Interface> * temp_interface = 0;
+      ModelHighAPI_Selection* temp_selection = 0;
+      std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>* temp_pair = 0;
+      std::shared_ptr<GeomAPI_Pnt2d> * temp_point = 0;
+      ModelHighAPI_RefAttr temp_refattr;
+      int newmem = 0;
+      int clearmem = 0;
+
+      for (std::list<PyObject*>::iterator it = temp_inputlist.begin(); it != temp_inputlist.end(); ++it) {
+        PyObject* item = *it;
+
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_selection, $descriptor(ModelHighAPI_Selection*), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_selection) {
+            temp_refattr = ModelHighAPI_RefAttr(std::shared_ptr<ModelAPI_Object>(temp_selection->resultSubShapePair().first));
+            if (newmem & SWIG_CAST_NEW_MEMORY) {
+              delete temp_selection;
+            }
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_attribute, $descriptor(std::shared_ptr<ModelAPI_Attribute> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_attribute) {
+            temp_refattr = ModelHighAPI_RefAttr(*temp_attribute);
+            if (newmem & SWIG_CAST_NEW_MEMORY) {
+              delete temp_attribute;
+            }
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_object, $descriptor(std::shared_ptr<ModelAPI_Object> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_object) {
+            temp_refattr = ModelHighAPI_RefAttr(*temp_object);
+            if (newmem & SWIG_CAST_NEW_MEMORY) {
+              delete temp_object;
+            }
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_interface, $descriptor(std::shared_ptr<ModelHighAPI_Interface> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_interface) {
+            temp_refattr = ModelHighAPI_RefAttr(*temp_interface);
+            if (newmem & SWIG_CAST_NEW_MEMORY) {
+              delete temp_interface;
+            }
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_pair, $descriptor(std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_pair) {
+            temp_point = &temp_pair->first;
+            temp_refattr = temp_pair->second;
+            if (newmem & SWIG_CAST_NEW_MEMORY) {
+              delete temp_pair;
+            }
+          }
+        } else
+        if (PyTuple_Check(item)) {
+          if (PyTuple_Size(item) == 2) {
+            double x = (double)PyFloat_AsDouble(PySequence_GetItem(item, 0));
+            double y = (double)PyFloat_AsDouble(PySequence_GetItem(item, 1));
+            temp_point = new std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(x, y));
+            clearmem = 1;
+          } else {
+            PyErr_SetString(PyExc_TypeError, "argument must a list of 2D points.");
+            return NULL;
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_point, $descriptor(std::shared_ptr<GeomAPI_Pnt2d> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          // fall through
+        } else
+        if (PyNumber_Check(item)) {
+          PyObject* item1 = *(++it);
+          if (PyNumber_Check(item1)) {
+            double x = (double)PyFloat_AsDouble(item);
+            double y = (double)PyFloat_AsDouble(item1);
+            temp_point = new std::shared_ptr<GeomAPI_Pnt2d>(new GeomAPI_Pnt2d(x, y));
+            clearmem = 1;
+          } else {
+            PyErr_SetString(PyExc_TypeError, "argument must a list of 2D points.");
+            return NULL;
+          }
+        }
+      }
+
+      if (temp_point || !temp_refattr.isEmpty()) {
+        if (temp_point) {
+          temp.push_back(std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>(*temp_point, temp_refattr));
+        } else {
+          temp.push_back(std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>(std::shared_ptr<GeomAPI_Pnt2d>(), temp_refattr));
+        }
+        if (temp_point && ((newmem & SWIG_CAST_NEW_MEMORY) || clearmem)) {
+          delete temp_point;
+        }
+      } else {
+        PyErr_SetString(PyExc_TypeError, "argument must be ModelHighAPI_RefAttr, ModelHighAPI_Selection, ModelHighAPI_Interface, ModelAPI_Attribute or ModelAPI_Object.");
+        return NULL;
+      }
+      Py_DECREF(item);
+    }
+    $1 = &temp;
+  } else {
+    PyErr_SetString(PyExc_ValueError, "argument must be a tuple of lists.");
+    return NULL;
+  }
+}
+
+%typecheck(SWIG_TYPECHECK_POINTER) std::list<std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> >, const std::list<std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> >& {
+  int newmem = 0;
+  if (PySequence_Check($input)) {
+    for (Py_ssize_t i = 0; i < PySequence_Size($input) && $1; ++i) {
+      PyObject * item = PySequence_GetItem($input, i);
+
+      std::list<PyObject*> temp_inputlist;
+      if (PySequence_Check(item)) {
+        for (Py_ssize_t i = 0; i < PySequence_Size(item); ++i) {
+          PyObject * tmpItem = PySequence_GetItem(item, i);
+          temp_inputlist.push_back(tmpItem);
+        }
+      } else {
+        temp_inputlist.push_back(item);
+      }
+
+      std::shared_ptr<ModelAPI_Attribute> * temp_attribute = 0;
+      std::shared_ptr<ModelAPI_Object> * temp_object = 0;
+      std::shared_ptr<ModelHighAPI_Interface> * temp_interface = 0;
+      ModelHighAPI_Selection* temp_selection = 0;
+      std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>* temp_pair = 0;
+      std::shared_ptr<GeomAPI_Pnt2d> * temp_point = 0;
+      ModelHighAPI_RefAttr temp_refattr;
+
+      $1 = 1;
+      for (std::list<PyObject*>::iterator it = temp_inputlist.begin(); it != temp_inputlist.end() && $1; ++it) {
+        PyObject* item = *it;
+
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_selection, $descriptor(ModelHighAPI_Selection*), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_selection) {
+            $1 = 1;
+          } else {
+            $1 = 0;
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_attribute, $descriptor(std::shared_ptr<ModelAPI_Attribute> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_attribute) {
+            $1 = 1;
+          } else {
+            $1 = 0;
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_object, $descriptor(std::shared_ptr<ModelAPI_Object> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_object) {
+            $1 = 1;
+          } else {
+            $1 = 0;
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_interface, $descriptor(std::shared_ptr<ModelHighAPI_Interface> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_interface) {
+            $1 = 1;
+          } else {
+            $1 = 0;
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_pair, $descriptor(std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_pair) {
+            $1 = 1;
+          } else {
+            $1 = 0;
+          }
+        } else
+        if (PyTuple_Check(item)) {
+          if (PyTuple_Size(item) == 2) {
+            if (PyNumber_Check(PySequence_GetItem(item, 0)) && PyNumber_Check(PySequence_GetItem(item, 1))) {
+              $1 = 1;
+            } else {
+              $1 = 0;
+            }
+          } else {
+            $1 = 0;
+          }
+        } else
+        if ((SWIG_ConvertPtrAndOwn(item, (void **)&temp_point, $descriptor(std::shared_ptr<GeomAPI_Pnt2d> *), SWIG_POINTER_EXCEPTION, &newmem)) == 0) {
+          if (temp_point) {
+            $1 = 1;
+          } else {
+            $1 = 0;
+          }
+        } else
+        if (PyNumber_Check(item)) {
+          $1 = 1;
+        } else {
+          $1 = 0;
+        }
+      }
+      Py_DECREF(item);
+    }
+  } else {
+    $1 = 0;
+  }
+}
+
+// fix compilarion error: 'res*' was not declared in this scope
+%typemap(freearg) const std::list<std::shared_ptr<GeomAPI_Pnt2d> > & {}
+
+
 // all supported interfaces (the order is very important according dependencies: base class first)
 %include "SketchAPI_SketchEntity.h"
 %include "SketchAPI_Point.h"
 %include "SketchAPI_MacroEllipse.h"
 %include "SketchAPI_EllipticArc.h"
 %include "SketchAPI_MacroEllipticArc.h"
+%include "SketchAPI_BSpline.h"
 %include "SketchAPI_Projection.h"
 %include "SketchAPI_Mirror.h"
 %include "SketchAPI_Translation.h"
diff --git a/src/SketchAPI/SketchAPI_BSpline.cpp b/src/SketchAPI/SketchAPI_BSpline.cpp
new file mode 100644 (file)
index 0000000..c89abac
--- /dev/null
@@ -0,0 +1,523 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "SketchAPI_BSpline.h"
+
+#include <GeomAPI_BSpline2d.h>
+#include <GeomAPI_Pnt2d.h>
+
+#include <GeomAlgoAPI_EdgeBuilder.h>
+
+#include <ModelHighAPI_Double.h>
+#include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Integer.h>
+#include <ModelHighAPI_Selection.h>
+#include <ModelHighAPI_Tools.h>
+
+#include <SketchPlugin_ConstraintCoincidenceInternal.h>
+#include <SketchPlugin_Line.h>
+#include <SketchPlugin_Point.h>
+
+#include <cmath>
+
+
+SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature> & theFeature)
+  : SketchAPI_SketchEntity(theFeature)
+{
+  initialize();
+}
+
+SketchAPI_BSpline::SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                     bool theInitialize)
+  : SketchAPI_SketchEntity(theFeature)
+{
+  if (theInitialize)
+    initialize();
+}
+
+SketchAPI_BSpline::~SketchAPI_BSpline()
+{
+}
+
+void SketchAPI_BSpline::setByDegreePolesAndWeights(const ModelHighAPI_Integer& theDegree,
+                                                   const std::list<GeomPnt2dPtr>& thePoles,
+                                                   const std::list<ModelHighAPI_Double>& theWeights)
+{
+  std::list<ModelHighAPI_Double> aWeights;
+  if (theWeights.size() <= 1) {
+    // prepare array of equal weights
+    aWeights.assign(thePoles.size(),
+        theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front());
+  }
+  else
+    aWeights = theWeights;
+
+  ModelHighAPI_Integer aDegree = theDegree;
+  std::list<ModelHighAPI_Double> aKnots;
+  std::list<ModelHighAPI_Integer> aMults;
+  getDefaultParameters(thePoles, aWeights, aDegree, aKnots, aMults);
+
+  setByParameters(aDegree, thePoles, aWeights, aKnots, aMults);
+}
+
+void SketchAPI_BSpline::setByParameters(const ModelHighAPI_Integer& theDegree,
+                                        const std::list<GeomPnt2dPtr>& thePoles,
+                                        const std::list<ModelHighAPI_Double>& theWeights,
+                                        const std::list<ModelHighAPI_Double>& theKnots,
+                                        const std::list<ModelHighAPI_Integer>& theMults)
+{
+  fillAttribute(theDegree, degree());
+
+  fillAttribute(thePoles, poles());
+  if (theWeights.size() <= 1) {
+    // prepare array of equal weights
+    std::list<ModelHighAPI_Double> aWeights(thePoles.size(),
+        theWeights.empty() ? ModelHighAPI_Double(1.0) : theWeights.front());
+    fillAttribute(aWeights, weights());
+  }
+  else
+    fillAttribute(theWeights, weights());
+
+  fillAttribute(theKnots, knots());
+  fillAttribute(theMults, multiplicities());
+
+  if (feature()->getKind() != SketchPlugin_BSplinePeriodic::ID())
+    setStartAndEndPoints();
+  execute();
+}
+
+void SketchAPI_BSpline::setStartAndEndPoints()
+{
+  fillAttribute(poles()->pnt(0), startPoint());
+  fillAttribute(poles()->pnt(poles()->size() - 1), endPoint());
+}
+
+void SketchAPI_BSpline::setByExternal(const ModelHighAPI_Selection & theExternal)
+{
+  fillAttribute(theExternal, external());
+  execute();
+}
+
+static CompositeFeaturePtr sketchForFeature(FeaturePtr theFeature)
+{
+  const std::set<AttributePtr>& aRefs = theFeature->data()->refsToMe();
+  for (std::set<AttributePtr>::const_iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt)
+    if ((*anIt)->id() == SketchPlugin_Sketch::FEATURES_ID())
+      return std::dynamic_pointer_cast<ModelAPI_CompositeFeature>((*anIt)->owner());
+  return CompositeFeaturePtr();
+}
+
+static void createInternalConstraint(const CompositeFeaturePtr& theSketch,
+                                     const AttributePoint2DPtr& thePoint,
+                                     const AttributePoint2DArrayPtr& thePoles,
+                                     const int thePoleIndex)
+{
+  FeaturePtr aConstraint = theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID());
+  aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint);
+  aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(thePoles);
+  aConstraint->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B())
+      ->setValue(thePoleIndex);
+  aConstraint->execute();
+}
+
+static void createPole(const CompositeFeaturePtr& theSketch,
+                       const FeaturePtr& theBSpline,
+                       const AttributePoint2DArrayPtr& thePoles,
+                       const int thePoleIndex,
+                       const bool theAuxiliary,
+                       std::list<FeaturePtr>& theEntities)
+{
+  GeomPnt2dPtr aPole = thePoles->pnt(thePoleIndex);
+
+  FeaturePtr aPointFeature = theSketch->addFeature(SketchPlugin_Point::ID());
+  AttributePoint2DPtr aCoord = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aPointFeature->attribute(SketchPlugin_Point::COORD_ID()));
+  aCoord->setValue(aPole);
+  aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline);
+  aPointFeature->execute();
+
+  std::ostringstream aName;
+  aName << theBSpline->name() << "_" << thePoles->id() << "_" << thePoleIndex;
+  aPointFeature->data()->setName(aName.str());
+  aPointFeature->lastResult()->data()->setName(aName.str());
+
+  aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(theAuxiliary);
+
+  createInternalConstraint(theSketch, aCoord, thePoles, thePoleIndex);
+
+  theEntities.push_back(aPointFeature);
+}
+
+static void createSegment(const CompositeFeaturePtr& theSketch,
+                          const FeaturePtr& theBSpline,
+                          const AttributePoint2DArrayPtr& thePoles,
+                          const int theStartPoleIndex,
+                          const bool theAuxiliary,
+                          std::list<FeaturePtr>& theEntities)
+{
+  int aEndPoleIndex = (theStartPoleIndex + 1) % thePoles->size();
+  GeomPnt2dPtr aStartPoint = thePoles->pnt(theStartPoleIndex);
+  GeomPnt2dPtr aEndPoint = thePoles->pnt(aEndPoleIndex);
+
+  FeaturePtr aLineFeature = theSketch->addFeature(SketchPlugin_Line::ID());
+  AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::START_ID()));
+  aLineStart->setValue(aStartPoint);
+  AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aLineFeature->attribute(SketchPlugin_Line::END_ID()));
+  aLineEnd->setValue(aEndPoint);
+  aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(theBSpline);
+  aLineFeature->execute();
+
+  std::ostringstream aName;
+  aName << theBSpline->name() << "_segment_" << theStartPoleIndex << "_" << aEndPoleIndex;
+  aLineFeature->data()->setName(aName.str());
+  aLineFeature->lastResult()->data()->setName(aName.str());
+
+  aLineFeature->boolean(SketchPlugin_Line::AUXILIARY_ID())->setValue(theAuxiliary);
+
+  createInternalConstraint(theSketch, aLineStart, thePoles, theStartPoleIndex);
+  createInternalConstraint(theSketch, aLineEnd, thePoles, aEndPoleIndex);
+
+  theEntities.push_back(aLineFeature);
+}
+
+static void toMapOfAuxIndices(const std::list<int>& theRegular,
+                              const std::list<int>& theAuxiliary,
+                              std::map<int, bool>& theIndices)
+{
+  for (auto it = theRegular.begin(); it != theRegular.end(); ++it)
+    theIndices[*it] = false;
+  for (auto it = theAuxiliary.begin(); it != theAuxiliary.end(); ++it)
+    theIndices[*it] = true;
+}
+
+std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPoles(
+    const std::list<int>& regular,
+    const std::list<int>& auxiliary) const
+{
+  std::map<int, bool> anAux;
+  toMapOfAuxIndices(regular, auxiliary, anAux);
+
+  std::list<FeaturePtr> anEntities;
+
+  FeaturePtr aBSpline = feature();
+  CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
+  AttributePoint2DArrayPtr aPoles = poles();
+
+  for (auto it = anAux.begin(); it != anAux.end(); ++it)
+    createPole(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
+
+  return SketchAPI_SketchEntity::wrap(anEntities);
+}
+
+std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_BSpline::controlPolygon(
+    const std::list<int>& regular,
+    const std::list<int>& auxiliary) const
+{
+  std::map<int, bool> anAux;
+  toMapOfAuxIndices(regular, auxiliary, anAux);
+
+  std::list<FeaturePtr> anEntities;
+
+  FeaturePtr aBSpline = feature();
+  CompositeFeaturePtr aSketch = sketchForFeature(aBSpline);
+  AttributePoint2DArrayPtr aPoles = poles();
+
+  for (auto it = anAux.begin(); it != anAux.end(); ++it)
+    createSegment(aSketch, aBSpline, aPoles, it->first, it->second, anEntities);
+
+  return SketchAPI_SketchEntity::wrap(anEntities);
+}
+
+
+void SketchAPI_BSpline::getDefaultParameters(
+    const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+    const std::list<ModelHighAPI_Double>& theWeights,
+    ModelHighAPI_Integer& theDegree,
+    std::list<ModelHighAPI_Double>& theKnots,
+    std::list<ModelHighAPI_Integer>& theMults) const
+{
+  std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve;
+  try {
+    std::list<double> aWeights;
+    for (std::list<ModelHighAPI_Double>::const_iterator it = theWeights.begin();
+         it != theWeights.end(); ++it)
+      aWeights.push_back(it->value());
+
+    bool isPeriodic = feature()->getKind() == SketchPlugin_BSplinePeriodic::ID();
+    if (theDegree.intValue() < 0)
+      aBSplineCurve.reset(new GeomAPI_BSpline2d(thePoles, aWeights, isPeriodic));
+    else {
+      aBSplineCurve.reset(new GeomAPI_BSpline2d(theDegree.intValue(), thePoles, aWeights,
+                                                std::list<double>(), std::list<int>(), isPeriodic));
+    }
+  }
+  catch (...) {
+    // cannot build a B-spline curve
+    return;
+  }
+
+  theDegree = aBSplineCurve->degree();
+  std::list<double> aKnots = aBSplineCurve->knots();
+  std::list<int> aMults = aBSplineCurve->mults();
+  theKnots.assign(aKnots.begin(), aKnots.end());
+  theMults.assign(aMults.begin(), aMults.end());
+}
+
+void SketchAPI_BSpline::checkDefaultParameters(bool& isDefaultDegree,
+                                               bool& isDefaultWeights,
+                                               bool& isDefaultKnotsMults) const
+{
+  static const double TOLERANCE = 1.e-7;
+
+  AttributePoint2DArrayPtr aPolesAttr = poles();
+  AttributeDoubleArrayPtr aWeightsAttr = weights();
+  AttributeDoubleArrayPtr aKnotsAttr = knots();
+  AttributeIntArrayPtr aMultsAttr = multiplicities();
+
+  std::list<GeomPnt2dPtr> aPoles;
+  std::list<ModelHighAPI_Double> aWeights;
+  isDefaultWeights = true;
+  for (int anIndex = 0; anIndex < aPolesAttr->size(); ++anIndex) {
+    aPoles.push_back(aPolesAttr->pnt(anIndex));
+    double aCurWeight = aWeightsAttr->value(anIndex);
+    isDefaultWeights = isDefaultWeights && fabs(aCurWeight - 1.0) < TOLERANCE;
+    aWeights.push_back(aCurWeight);
+  }
+
+  ModelHighAPI_Integer aDegree(-1);
+  std::list<ModelHighAPI_Double> aKnots;
+  std::list<ModelHighAPI_Integer> aMults;
+  getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults);
+  isDefaultDegree = aDegree.intValue() == degree()->value();
+  if (!isDefaultDegree) {
+    // recalculate knots and multiplicities with the actual degree
+    aDegree = degree()->value();
+    getDefaultParameters(aPoles, aWeights, aDegree, aKnots, aMults);
+  }
+
+  isDefaultKnotsMults = aKnotsAttr->size() == (int)aKnots.size()
+                     && aMultsAttr->size() == (int)aMults.size();
+  if (isDefaultKnotsMults) {
+    std::list<ModelHighAPI_Double>::iterator anIt = aKnots.begin();
+    for (int anIndex = 0; isDefaultKnotsMults && anIt != aKnots.end(); ++anIt, ++anIndex)
+      isDefaultKnotsMults = fabs(anIt->value() - aKnotsAttr->value(anIndex)) < TOLERANCE;
+  }
+  if (isDefaultKnotsMults) {
+    std::list<ModelHighAPI_Integer>::iterator anIt = aMults.begin();
+    for (int anIndex = 0; isDefaultKnotsMults && anIt != aMults.end(); ++anIt, ++anIndex)
+      isDefaultKnotsMults = anIt->intValue() == aMultsAttr->value(anIndex);
+  }
+
+  isDefaultDegree = isDefaultDegree && isDefaultKnotsMults;
+  isDefaultWeights = isDefaultWeights && isDefaultKnotsMults;
+}
+
+
+static void bsplineAuxiliaryFeature(const AttributeRefAttrPtr& theReference,
+                                    FeaturePtr& thePoint,
+                                    FeaturePtr& theSegment)
+{
+  ObjectPtr anAuxObject;
+  if (theReference->isObject())
+    anAuxObject = theReference->object();
+  else
+    anAuxObject = theReference->attr()->owner();
+
+  FeaturePtr anAuxFeature = ModelAPI_Feature::feature(anAuxObject);
+  if (anAuxFeature->getKind() == SketchPlugin_Point::ID())
+    thePoint = anAuxFeature;
+  else if (anAuxFeature->getKind() == SketchPlugin_Line::ID() &&
+           theReference->attr()->id() == SketchPlugin_Line::START_ID()) {
+    // process only coincidence with start point
+    theSegment = anAuxFeature;
+  }
+}
+
+static void collectAuxiliaryFeatures(FeaturePtr theBSpline,
+                                     std::map<int, FeaturePtr>& thePoints,
+                                     std::map<int, FeaturePtr>& theSegments)
+{
+  const std::set<AttributePtr>& aRefs = theBSpline->data()->refsToMe();
+  for (std::set<AttributePtr>::const_iterator aRefIt = aRefs.begin();
+       aRefIt != aRefs.end(); ++aRefIt) {
+    FeaturePtr anOwner = ModelAPI_Feature::feature((*aRefIt)->owner());
+    if (anOwner->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
+      // process internal constraints only
+      AttributeRefAttrPtr aRefAttrA = anOwner->refattr(SketchPlugin_Constraint::ENTITY_A());
+      AttributeRefAttrPtr aRefAttrB = anOwner->refattr(SketchPlugin_Constraint::ENTITY_B());
+      AttributePtr anAttrA = aRefAttrA->attr();
+      AttributePtr anAttrB = aRefAttrB->attr();
+
+      AttributeIntegerPtr aPoleIndex;
+      FeaturePtr aPoint, aLine;
+      if (anAttrA && anAttrA->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
+        aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A());
+        bsplineAuxiliaryFeature(aRefAttrB, aPoint, aLine);
+      }
+      else if (anAttrB && anAttrB->attributeType() == GeomDataAPI_Point2DArray::typeId()) {
+        aPoleIndex = anOwner->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
+        bsplineAuxiliaryFeature(aRefAttrA, aPoint, aLine);
+      }
+
+      if (aPoint)
+        thePoints[aPoleIndex->value()] = aPoint;
+      else if (aLine)
+        theSegments[aPoleIndex->value()] = aLine;
+    }
+  }
+}
+
+void SketchAPI_BSpline::dump(ModelHighAPI_Dumper& theDumper) const
+{
+  if (isCopy())
+    return; // no need to dump copied feature
+
+  FeaturePtr aBase = feature();
+  const std::string& aSketchName = theDumper.parentName(aBase);
+
+  AttributeSelectionPtr anExternal = aBase->selection(SketchPlugin_SketchEntity::EXTERNAL_ID());
+  if (anExternal->context()) {
+    // B-spline is external
+    theDumper << aBase << " = " << aSketchName << ".addSpline(" << anExternal << ")" << std::endl;
+  } else {
+    // check if some B-spline parameters are default and should not be dumped
+    bool isDefaultDegree, isDefaultWeights, isDefaultKnotsMults;
+    checkDefaultParameters(isDefaultDegree, isDefaultWeights, isDefaultKnotsMults);
+
+    theDumper << aBase << " = " << aSketchName << ".addSpline(";
+    if (!isDefaultDegree)
+      theDumper << "degree = " << degree() << ", ";
+    theDumper << "poles = " << poles();
+    if (!isDefaultWeights)
+      theDumper << ", weights = " << weights();
+    if (!isDefaultKnotsMults)
+      theDumper << ", knots = " << knots() << ", multiplicities = " << multiplicities();
+    if (aBase->getKind() == SketchPlugin_BSplinePeriodic::ID())
+      theDumper << ", periodic = True";
+    theDumper << ")" << std::endl;
+  }
+  // dump "auxiliary" flag if necessary
+  SketchAPI_SketchEntity::dump(theDumper);
+
+  // dump control polygon
+  std::map<int, FeaturePtr> anAuxPoles, anAuxSegments;
+  collectAuxiliaryFeatures(aBase, anAuxPoles, anAuxSegments);
+
+  if (!anAuxPoles.empty())
+    dumpControlPolygon(theDumper, aBase, "controlPoles", anAuxPoles);
+  if (!anAuxSegments.empty())
+    dumpControlPolygon(theDumper, aBase, "controlPolygon", anAuxSegments);
+}
+
+static void dumpList(ModelHighAPI_Dumper& theDumper,
+                     const std::string& theAttrName,
+                     const std::set<int>& theIndices)
+{
+  theDumper << theAttrName << " = [";
+  std::set<int>::const_iterator it = theIndices.begin();
+  theDumper << *it;
+  for (++it; it != theIndices.end(); ++it)
+    theDumper << ", " << *it;
+  theDumper << "]";
+}
+
+void SketchAPI_BSpline::dumpControlPolygon(
+    ModelHighAPI_Dumper& theDumper,
+    const FeaturePtr& theBSpline,
+    const std::string& theMethod,
+    const std::map<int, FeaturePtr>& theAuxFeatures) const
+{
+  theDumper << "[";
+  bool isFirst = true;
+  // dump features and split them to auxiliary and regular
+  std::set<int> aRegular, anAuxiliary;
+  for (std::map<int, FeaturePtr>::const_iterator it = theAuxFeatures.begin();
+       it != theAuxFeatures.end(); ++it) {
+    if (!isFirst)
+      theDumper << ", ";
+    theDumper << theDumper.name(it->second, false);
+    theDumper.doNotDumpFeature(it->second);
+    isFirst = false;
+
+    if (it->second->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
+      anAuxiliary.insert(it->first);
+    else
+      aRegular.insert(it->first);
+  }
+  theDumper << "] = " << theDumper.name(theBSpline) << "." << theMethod << "(";
+  if (!aRegular.empty()) {
+    dumpList(theDumper, "regular", aRegular);
+    if (!anAuxiliary.empty())
+      theDumper << ", ";
+  }
+  if (!anAuxiliary.empty())
+    dumpList(theDumper, "auxiliary", anAuxiliary);
+  theDumper << ")" << std::endl;
+}
+
+static void setCoordinates(const FeaturePtr& theFeature,
+                           const std::string& theAttrName,
+                           const GeomPnt2dPtr& theCoordinates)
+{
+  AttributePoint2DPtr aPoint =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theFeature->attribute(theAttrName));
+  aPoint->setValue(theCoordinates);
+}
+
+bool SketchAPI_BSpline::insertPole(const int theIndex,
+                                   const GeomPnt2dPtr& theCoordinates,
+                                   const ModelHighAPI_Double& theWeight)
+{
+  std::ostringstream anActionName;
+  anActionName << SketchPlugin_BSplineBase::ADD_POLE_ACTION_ID() << "#" << theIndex;
+  bool isOk = feature()->customAction(anActionName.str());
+  if (isOk) {
+    int anIndex = theIndex + 1;
+    if (feature()->getKind() == SketchPlugin_BSpline::ID() && anIndex + 1 >= poles()->size())
+      anIndex = poles()->size() - 2;
+    // initialize coordinates and weight of new pole
+    poles()->setPnt(anIndex, theCoordinates);
+    weights()->setValue(anIndex, theWeight.value());
+
+    // update coordinates of points of control polygon
+    std::map<int, FeaturePtr> aPoints, aLines;
+    collectAuxiliaryFeatures(feature(), aPoints, aLines);
+    std::map<int, FeaturePtr>::iterator aFound = aPoints.find(anIndex);
+    if (aFound != aPoints.end())
+      setCoordinates(aFound->second, SketchPlugin_Point::COORD_ID(), theCoordinates);
+    aFound = aLines.find(anIndex);
+    if (aFound != aLines.end())
+      setCoordinates(aFound->second, SketchPlugin_Line::START_ID(), theCoordinates);
+    aFound = aLines.find(anIndex - 1);
+    if (aFound != aLines.end())
+      setCoordinates(aFound->second, SketchPlugin_Line::END_ID(), theCoordinates);
+  }
+  return isOk;
+}
+
+
+
+// =================================================================================================
+SketchAPI_BSplinePeriodic::SketchAPI_BSplinePeriodic(const FeaturePtr& theFeature)
+  : SketchAPI_BSpline(theFeature, false)
+{
+  initialize();
+}
diff --git a/src/SketchAPI/SketchAPI_BSpline.h b/src/SketchAPI/SketchAPI_BSpline.h
new file mode 100644 (file)
index 0000000..0564625
--- /dev/null
@@ -0,0 +1,160 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketchAPI_BSpline_H_
+#define SketchAPI_BSpline_H_
+
+#include "SketchAPI.h"
+#include "SketchAPI_SketchEntity.h"
+
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <ModelAPI_AttributeDoubleArray.h>
+
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
+
+#include <ModelHighAPI_Double.h>
+#include <ModelHighAPI_Integer.h>
+
+class ModelHighAPI_Selection;
+
+/// \class SketchAPI_BSpline
+/// \ingroup CPPHighAPI
+/// \brief Interface for BSpline feature.
+class SketchAPI_BSpline : public SketchAPI_SketchEntity
+{
+public:
+  /// Constructor without values.
+  SKETCHAPI_EXPORT
+  explicit SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature);
+
+  /// Destructor.
+  SKETCHAPI_EXPORT
+  virtual ~SketchAPI_BSpline();
+
+  INTERFACE_8(SketchPlugin_BSpline::ID(),
+              poles, SketchPlugin_BSplineBase::POLES_ID(),
+              GeomDataAPI_Point2DArray, /** B-spline poles */,
+              weights, SketchPlugin_BSplineBase::WEIGHTS_ID(),
+              ModelAPI_AttributeDoubleArray, /** B-spline weights */,
+              knots, SketchPlugin_BSplineBase::KNOTS_ID(),
+              ModelAPI_AttributeDoubleArray, /** B-spline knots */,
+              multiplicities, SketchPlugin_BSplineBase::MULTS_ID(),
+              ModelAPI_AttributeIntArray, /** Knots multiplicities */,
+              degree, SketchPlugin_BSplineBase::DEGREE_ID(),
+              ModelAPI_AttributeInteger, /** B-spline degree */,
+              startPoint, SketchPlugin_BSpline::START_ID(),
+              GeomDataAPI_Point2D, /** First pole of B-spline */,
+              endPoint, SketchPlugin_BSpline::END_ID(),
+              GeomDataAPI_Point2D, /** Last pole of B-spline */,
+              external, SketchPlugin_BSplineBase::EXTERNAL_ID(),
+              ModelAPI_AttributeSelection, /** External */)
+
+  /// Set by poles and weights.
+  SKETCHAPI_EXPORT
+  void setByDegreePolesAndWeights(const ModelHighAPI_Integer& theDegree,
+                                  const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                                  const std::list<ModelHighAPI_Double>& theWeights);
+
+  /// Initialize by full set of B-spline parameters.
+  SKETCHAPI_EXPORT
+  void setByParameters(const ModelHighAPI_Integer& theDegree,
+                       const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                       const std::list<ModelHighAPI_Double>& theWeights,
+                       const std::list<ModelHighAPI_Double>& theKnots,
+                       const std::list<ModelHighAPI_Integer>& theMults);
+
+  /// Set by external.
+  SKETCHAPI_EXPORT
+  void setByExternal(const ModelHighAPI_Selection& theExternal);
+
+  /// Generate list of construction points coincident with B-spline poles
+  SKETCHAPI_EXPORT
+  std::list<std::shared_ptr<SketchAPI_SketchEntity> > controlPoles(
+      const std::list<int>& regular   = std::list<int>(),
+      const std::list<int>& auxiliary = std::list<int>()) const;
+
+  /// Generate control polygon for B-spline curve
+  SKETCHAPI_EXPORT
+  std::list<std::shared_ptr<SketchAPI_SketchEntity> > controlPolygon(
+      const std::list<int>& regular   = std::list<int>(),
+      const std::list<int>& auxiliary = std::list<int>()) const;
+
+  /// Insert new pole after the pole with the given index
+  SKETCHAPI_EXPORT
+  bool insertPole(const int theIndex,
+                  const std::shared_ptr<GeomAPI_Pnt2d>& theCoordinates,
+                  const ModelHighAPI_Double& theWeight = ModelHighAPI_Double(1.0));
+
+  /// Dump wrapped feature
+  SKETCHAPI_EXPORT
+  virtual void dump(ModelHighAPI_Dumper& theDumper) const;
+
+protected:
+  SketchAPI_BSpline(const std::shared_ptr<ModelAPI_Feature>& theFeature, bool theInitialize);
+
+private:
+  /// Initialize start and end points of B-spline and apply internal coincidence
+  /// constraint to keep them on the corresponding pole.
+  void setStartAndEndPoints();
+
+  /// Compute default B-spline parameters (degree, knots and multiplicities)
+  /// basing on hte given poles and weights
+  void getDefaultParameters(const std::list<std::shared_ptr<GeomAPI_Pnt2d> >& thePoles,
+                            const std::list<ModelHighAPI_Double>& theWeights,
+                            ModelHighAPI_Integer& theDegree,
+                            std::list<ModelHighAPI_Double>& theKnots,
+                            std::list<ModelHighAPI_Integer>& theMults) const;
+
+  /// Check what parameters of B-spline are default
+  void checkDefaultParameters(bool& isDefaultDegree,
+                              bool& isDefaultWeights,
+                              bool& isDefaultKnotsMults) const;
+
+  void dumpControlPolygon(ModelHighAPI_Dumper& theDumper,
+                          const FeaturePtr& theBSpline,
+                          const std::string& theMethod,
+                          const std::map<int, FeaturePtr>& theAuxFeatures) const;
+};
+
+/// Pointer on B-spline object.
+typedef std::shared_ptr<SketchAPI_BSpline> BSplinePtr;
+
+
+
+/// \class SketchAPI_BSplinePeriodic
+/// \ingroup CPPHighAPI
+/// \brief Interface for BSplinePeriodic feature.
+class SketchAPI_BSplinePeriodic : public SketchAPI_BSpline
+{
+public:
+  /// Constructor without values.
+  SKETCHAPI_EXPORT
+  explicit SketchAPI_BSplinePeriodic(const std::shared_ptr<ModelAPI_Feature>& theFeature);
+
+  /// Destructor.
+  SKETCHAPI_EXPORT
+  virtual ~SketchAPI_BSplinePeriodic() {}
+
+  static std::string ID() { return SketchPlugin_BSplinePeriodic::ID(); }
+  virtual std::string getID() { return SketchPlugin_BSplinePeriodic::ID(); }
+};
+
+#endif // SketchAPI_BSpline_H_
index b1fefb5e25c945e95743b65687b36b8237be8834..7ff2457a9b5f6b86e1e3fc1bc2458b65ee674927 100644 (file)
@@ -156,19 +156,6 @@ static const std::string& constraintTypeToSetter(const std::string& theType)
   return DUMMY;
 }
 
-static std::string angleTypeToString(int theAngleType)
-{
-  switch (theAngleType) {
-  case SketcherPrs_Tools::ANGLE_COMPLEMENTARY:
-    return std::string("Complementary");
-  case SketcherPrs_Tools::ANGLE_BACKWARD:
-    return std::string("Backward");
-  default:
-    break;
-  }
-  return std::string();
-}
-
 bool SketchAPI_Constraint::areAllAttributesDumped(ModelHighAPI_Dumper& theDumper) const
 {
   bool areAttributesDumped = true;
index 9d7bf8c461624a42bc42c358410b577ffc147158..4f5eedf235d63b2a44b9977a4256a1ce68f90d09 100644 (file)
@@ -76,18 +76,18 @@ static void calculatePossibleValuesOfAngle(FeaturePtr theFeature,
 
   std::shared_ptr<GeomAPI_Angle2d> anAng(new GeomAPI_Angle2d(
       aStartA->pnt(), aEndA->pnt(), aStartB->pnt(), aEndB->pnt()));
-  theAngleDirect = anAng->angleDegree();
+  theAngleDirect = fabs(anAng->angleDegree());
+  theAngleComplementary = 180.0 - theAngleDirect;
   theAngleBackward = 360.0 - theAngleDirect;
-
-  if (theAngleDirect > 180.0)
-    theAngleComplementary = theAngleDirect - 180.0;
-  else
-    theAngleComplementary = 180.0 - theAngleDirect;
 }
 
 static std::string angleTypeToString(FeaturePtr theFeature)
 {
   static const double TOLERANCE = 1.e-7;
+  static const std::string THE_ANGLE_DIRECT("Direct");
+  static const std::string THE_ANGLE_SUPPLEMENTARY("Supplementary");
+  static const std::string THE_ANGLE_BACKWARD("Backward");
+
   double anAngleDirect, anAngleComplmentary, anAngleBackward;
   calculatePossibleValuesOfAngle(theFeature, anAngleDirect, anAngleComplmentary, anAngleBackward);
 
@@ -104,18 +104,17 @@ static std::string angleTypeToString(FeaturePtr theFeature)
   // find the minimal difference
   std::string aType;
   if (isDirect && aDirectDiff < TOLERANCE) {
-    // Nothing to do.
-    // This case is empty and going the first to check the direct angle before the others.
+    aType = THE_ANGLE_DIRECT;
   }
   else if (isComplementary && aComplementaryDiff < TOLERANCE)
-    aType = "Complementary";
+    aType = THE_ANGLE_SUPPLEMENTARY;
   else if (isBackward && aBackwardDiff < TOLERANCE)
-    aType = "Backward";
+    aType = THE_ANGLE_BACKWARD;
   else {
     if (aComplementaryDiff < aDirectDiff && aComplementaryDiff < aBackwardDiff)
-      aType = "Complementary";
+      aType = THE_ANGLE_SUPPLEMENTARY;
     else if (aBackwardDiff < aDirectDiff && aBackwardDiff < aComplementaryDiff)
-      aType = "Backward";
+      aType = THE_ANGLE_BACKWARD;
   }
   return aType;
 }
@@ -128,10 +127,9 @@ void SketchAPI_ConstraintAngle::dump(ModelHighAPI_Dumper& theDumper) const
 
   // calculate angle value as it was just applied to the attributes
   FeaturePtr aBase = feature();
-  std::string aSetterSuffix = angleTypeToString(aBase);
 
   const std::string& aSketchName = theDumper.parentName(aBase);
-  theDumper << aBase << " = " << aSketchName << "." << "setAngle" << aSetterSuffix << "(";
+  theDumper << aBase << " = " << aSketchName << "." << "setAngle(";
 
   bool isFirstAttr = true;
   for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
@@ -146,5 +144,6 @@ void SketchAPI_ConstraintAngle::dump(ModelHighAPI_Dumper& theDumper) const
   if (aValueAttr && aValueAttr->isInitialized())
     theDumper << ", " << aValueAttr;
 
-  theDumper << ")" << std::endl;
+  std::string aType = angleTypeToString(aBase);
+  theDumper << ", type = \"" << aType << "\")" << std::endl;
 }
index 4baf94150d033d352ab0027122a39bbae3822ffd..aa94fa895e1b89607d0198dafb243b84d94f2ef9 100644 (file)
 #include "SketchAPI_Projection.h"
 
 #include <SketchPlugin_Line.h>
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_Ellipse.h>
 #include <SketchPlugin_EllipticArc.h>
 #include <SketchPlugin_Point.h>
 
 #include <SketchAPI_Arc.h>
+#include <SketchAPI_BSpline.h>
 #include <SketchAPI_Circle.h>
 #include <SketchAPI_Ellipse.h>
 #include <SketchAPI_EllipticArc.h>
@@ -108,6 +111,10 @@ std::shared_ptr<SketchAPI_SketchEntity> SketchAPI_Projection::createdFeature() c
     anEntity.reset(new SketchAPI_Ellipse(aProjectedFeature));
   else if (aProjectedFeature->getKind() == SketchPlugin_EllipticArc::ID())
     anEntity.reset(new SketchAPI_EllipticArc(aProjectedFeature));
+  else if (aProjectedFeature->getKind() == SketchPlugin_BSpline::ID())
+    anEntity.reset(new SketchAPI_BSpline(aProjectedFeature));
+  else if (aProjectedFeature->getKind() == SketchPlugin_BSplinePeriodic::ID())
+    anEntity.reset(new SketchAPI_BSplinePeriodic(aProjectedFeature));
   else if (aProjectedFeature->getKind() == SketchPlugin_Point::ID())
     anEntity.reset(new SketchAPI_Point(aProjectedFeature));
 
index b2ca766988edd68862c7c6894ec35672e5de608e..7b472113e72a508ca7e994bf0a65629c18d8d4e1 100644 (file)
@@ -40,6 +40,7 @@
 #include <SketchPlugin_Split.h>
 #include <SketchPlugin_ConstraintTangent.h>
 #include <SketchPlugin_ConstraintVertical.h>
+#include <SketchPlugin_MacroBSpline.h>
 #include <SketcherPrs_Tools.h>
 //--------------------------------------------------------------------------------------
 #include <ModelAPI_Events.h>
@@ -53,6 +54,7 @@
 #include <ModelHighAPI_Tools.h>
 //--------------------------------------------------------------------------------------
 #include "SketchAPI_Arc.h"
+#include "SketchAPI_BSpline.h"
 #include "SketchAPI_Circle.h"
 #include "SketchAPI_Ellipse.h"
 #include "SketchAPI_EllipticArc.h"
@@ -75,6 +77,8 @@
 #include <GeomAPI_ShapeExplorer.h>
 #include <GeomAPI_XY.h>
 #include <GeomAlgoAPI_SketchBuilder.h>
+
+#include <algorithm>
 #include <cmath>
 //--------------------------------------------------------------------------------------
 SketchAPI_Sketch::SketchAPI_Sketch(
@@ -604,9 +608,9 @@ std::shared_ptr<SketchAPI_MacroEllipse> SketchAPI_Sketch::addEllipse(
 }
 
 std::shared_ptr<SketchAPI_MacroEllipse> SketchAPI_Sketch::addEllipse(
-    const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& thePoint1,
-    const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& thePoint2,
-    const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& thePassedPoint,
+    const PointOrReference& thePoint1,
+    const PointOrReference& thePoint2,
+    const PointOrReference& thePassedPoint,
     bool isPoint1Center)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
@@ -663,10 +667,10 @@ std::shared_ptr<SketchAPI_EllipticArc> SketchAPI_Sketch::addEllipticArc(
 }
 
 std::shared_ptr<SketchAPI_MacroEllipticArc> SketchAPI_Sketch::addEllipticArc(
-    const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theCenter,
-    const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theMajorAxisPoint,
-    const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theStartPoint,
-    const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theEndPoint,
+    const PointOrReference& theCenter,
+    const PointOrReference& theMajorAxisPoint,
+    const PointOrReference& theStartPoint,
+    const PointOrReference& theEndPoint,
     bool theInversed)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
@@ -695,6 +699,82 @@ std::shared_ptr<SketchAPI_EllipticArc> SketchAPI_Sketch::addEllipticArc(
   return EllipticArcPtr(new SketchAPI_EllipticArc(aFeature, theExternalName));
 }
 
+//--------------------------------------------------------------------------------------
+
+std::shared_ptr<SketchAPI_BSpline> SketchAPI_Sketch::addSpline(
+    const ModelHighAPI_Selection & external,
+    const int degree,
+    const std::list<PointOrReference>& poles,
+    const std::list<ModelHighAPI_Double>& weights,
+    const std::list<ModelHighAPI_Double>& knots,
+    const std::list<ModelHighAPI_Integer>& multiplicities,
+    const bool periodic)
+{
+  // split poles and references to other shapes
+  bool hasReference = false;
+  std::list<GeomPnt2dPtr> aPoints;
+  std::list<ModelHighAPI_RefAttr> aReferences;
+  for (std::list<PointOrReference>::const_iterator it = poles.begin(); it != poles.end(); ++it) {
+    aPoints.push_back(it->first);
+    aReferences.push_back(it->second);
+    if (!it->second.isEmpty())
+      hasReference = true;
+  }
+
+  BSplinePtr aBSpline;
+  CompositeFeaturePtr aSketch = compositeFeature();
+  if (hasReference) {
+    // use macro-feature to create coincidences to referred features
+    FeaturePtr aMacroFeature = aSketch->addFeature(
+        periodic ? SketchPlugin_MacroBSplinePeriodic::ID() : SketchPlugin_MacroBSpline::ID());
+    AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+        aMacroFeature->attribute(SketchPlugin_MacroBSpline::POLES_ID()));
+    AttributeDoubleArrayPtr aWeightsAttr =
+        aMacroFeature->data()->realArray(SketchPlugin_MacroBSpline::WEIGHTS_ID());
+    AttributeRefAttrListPtr aPolesRefAttr =
+        aMacroFeature->data()->refattrlist(SketchPlugin_MacroBSpline::REF_POLES_ID());
+    // always generate a control polygon to apply coincidences correctly
+    aMacroFeature->boolean(SketchPlugin_MacroBSpline::CONTROL_POLYGON_ID())->setValue(true);
+    // initialize B-spline attributes
+    fillAttribute(aPoints, aPolesAttr);
+    if (weights.empty())
+      fillAttribute(std::list<ModelHighAPI_Double>(poles.size(), 1.0), aWeightsAttr);
+    else
+      fillAttribute(weights, aWeightsAttr);
+    fillAttribute(aReferences, aPolesRefAttr);
+    apply(); // to kill macro-feature
+
+    // find created B-spline feature
+    const std::string& aKindToFind =
+        periodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID();
+    int aNbSubs = aSketch->numberOfSubs();
+    for (int anIndex = aNbSubs - 1; anIndex >= 0; --anIndex) {
+      FeaturePtr aFeature = aSketch->subFeature(anIndex);
+      if (aFeature->getKind() == aKindToFind) {
+        aBSpline.reset(periodic ? new SketchAPI_BSplinePeriodic(aFeature)
+                                : new SketchAPI_BSpline(aFeature));
+        aBSpline->execute();
+        break;
+      }
+    }
+  }
+  else {
+    // compute B-spline by parameters
+    FeaturePtr aFeature = aSketch->addFeature(
+        periodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID());
+
+    aBSpline.reset(periodic ? new SketchAPI_BSplinePeriodic(aFeature)
+                            : new SketchAPI_BSpline(aFeature));
+    if (external.variantType() != ModelHighAPI_Selection::VT_Empty)
+      aBSpline->setByExternal(external);
+    else if (knots.empty() || multiplicities.empty())
+      aBSpline->setByDegreePolesAndWeights(degree, aPoints, weights);
+    else
+      aBSpline->setByParameters(degree, aPoints, weights, knots, multiplicities);
+  }
+  return aBSpline;
+}
+
 //--------------------------------------------------------------------------------------
 std::shared_ptr<SketchAPI_Projection> SketchAPI_Sketch::addProjection(
     const ModelHighAPI_Selection & theExternalFeature,
@@ -798,16 +878,38 @@ std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::addTrim(
 std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::setAngle(
     const ModelHighAPI_RefAttr & theLine1,
     const ModelHighAPI_RefAttr & theLine2,
-    const ModelHighAPI_Double & theValue)
+    const ModelHighAPI_Double & theValue,
+    const std::string& theType)
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
       compositeFeature()->addFeature(SketchPlugin_ConstraintAngle::ID());
-  fillAttribute(SketcherPrs_Tools::ANGLE_DIRECT,
-      aFeature->integer(SketchPlugin_ConstraintAngle::TYPE_ID()));
-  // fill the value before llines to avoid calculation of angle value by the Angle feature
+
+  const int aVersion = theType.empty() ? SketchPlugin_ConstraintAngle::THE_VERSION_0
+                                       : SketchPlugin_ConstraintAngle::THE_VERSION_1;
+  fillAttribute(aVersion, aFeature->integer(SketchPlugin_ConstraintAngle::VERSION_ID()));
+
+  int aType = (int)SketcherPrs_Tools::ANGLE_DIRECT;
+  fillAttribute(aType, aFeature->integer(SketchPlugin_ConstraintAngle::PREV_TYPE_ID()));
+  fillAttribute(aType, aFeature->integer(SketchPlugin_ConstraintAngle::TYPE_ID()));
+
+  if (aVersion == SketchPlugin_ConstraintAngle::THE_VERSION_0)
+    fillAttribute(theValue, aFeature->real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
+
   fillAttribute(theLine1, aFeature->refattr(SketchPlugin_Constraint::ENTITY_A()));
   fillAttribute(theLine2, aFeature->refattr(SketchPlugin_Constraint::ENTITY_B()));
-  fillAttribute(theValue, aFeature->real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
+
+  if (aVersion == SketchPlugin_ConstraintAngle::THE_VERSION_1) {
+    std::string aTypeLC = theType;
+    std::transform(aTypeLC.begin(), aTypeLC.end(), aTypeLC.begin(), ::tolower);
+    if (aTypeLC == "supplementary")
+      aType = (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY;
+    else if (aTypeLC == "backward")
+      aType = (int)SketcherPrs_Tools::ANGLE_BACKWARD;
+
+    fillAttribute(aType, aFeature->integer(SketchPlugin_ConstraintAngle::TYPE_ID()));
+    fillAttribute(theValue, aFeature->real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
+  }
+
   aFeature->execute();
   return InterfacePtr(new ModelHighAPI_Interface(aFeature));
 }
@@ -819,11 +921,13 @@ std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::setAngleComplementary(
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
       compositeFeature()->addFeature(SketchPlugin_ConstraintAngle::ID());
+  fillAttribute(SketchPlugin_ConstraintAngle::THE_VERSION_0,
+      aFeature->integer(SketchPlugin_ConstraintAngle::VERSION_ID()));
   fillAttribute(SketcherPrs_Tools::ANGLE_COMPLEMENTARY,
       aFeature->integer(SketchPlugin_ConstraintAngle::TYPE_ID()));
+  fillAttribute(theValue, aFeature->real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
   fillAttribute(theLine1, aFeature->refattr(SketchPlugin_Constraint::ENTITY_A()));
   fillAttribute(theLine2, aFeature->refattr(SketchPlugin_Constraint::ENTITY_B()));
-  fillAttribute(theValue, aFeature->real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
   aFeature->execute();
   return InterfacePtr(new ModelHighAPI_Interface(aFeature));
 }
@@ -835,11 +939,13 @@ std::shared_ptr<ModelHighAPI_Interface> SketchAPI_Sketch::setAngleBackward(
 {
   std::shared_ptr<ModelAPI_Feature> aFeature =
       compositeFeature()->addFeature(SketchPlugin_ConstraintAngle::ID());
+  fillAttribute(SketchPlugin_ConstraintAngle::THE_VERSION_0,
+      aFeature->integer(SketchPlugin_ConstraintAngle::VERSION_ID()));
   fillAttribute(SketcherPrs_Tools::ANGLE_BACKWARD,
       aFeature->integer(SketchPlugin_ConstraintAngle::TYPE_ID()));
+  fillAttribute(theValue, aFeature->real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
   fillAttribute(theLine1, aFeature->refattr(SketchPlugin_Constraint::ENTITY_A()));
   fillAttribute(theLine2, aFeature->refattr(SketchPlugin_Constraint::ENTITY_B()));
-  fillAttribute(theValue, aFeature->real(SketchPlugin_ConstraintAngle::ANGLE_VALUE_ID()));
   aFeature->execute();
   return InterfacePtr(new ModelHighAPI_Interface(aFeature));
 }
@@ -1156,7 +1262,16 @@ static std::shared_ptr<GeomAPI_Pnt2d> pointOnEllipse(const FeaturePtr& theFeatur
   return aMajorAxisEnd ? aMajorAxisEnd->pnt() : std::shared_ptr<GeomAPI_Pnt2d>();
 }
 
-static std::shared_ptr<GeomAPI_Pnt2d> middlePoint(const ObjectPtr& theObject)
+static std::shared_ptr<GeomAPI_Pnt2d> middlePointOnBSpline(const FeaturePtr& theFeature,
+                                                           SketchAPI_Sketch* theSketch)
+{
+  GeomAPI_Edge anEdge(theFeature->lastResult()->shape());
+  GeomPointPtr aMiddle = anEdge.middlePoint();
+  return theSketch->to2D(aMiddle);
+}
+
+static std::shared_ptr<GeomAPI_Pnt2d> middlePoint(const ObjectPtr& theObject,
+                                                  SketchAPI_Sketch* theSketch)
 {
   std::shared_ptr<GeomAPI_Pnt2d> aMiddlePoint;
   FeaturePtr aFeature = ModelAPI_Feature::feature(theObject);
@@ -1175,6 +1290,9 @@ static std::shared_ptr<GeomAPI_Pnt2d> middlePoint(const ObjectPtr& theObject)
       aMiddlePoint = pointOnEllipse(aFeature);
     else if (aFeatureKind == SketchPlugin_EllipticArc::ID())
       aMiddlePoint = pointOnEllipse(aFeature, false);
+    else if (aFeatureKind == SketchPlugin_BSpline::ID() ||
+             aFeatureKind == SketchPlugin_BSplinePeriodic::ID())
+      aMiddlePoint = middlePointOnBSpline(aFeature, theSketch);
   }
   return aMiddlePoint;
 }
@@ -1189,7 +1307,7 @@ void SketchAPI_Sketch::move(const ModelHighAPI_RefAttr& theMovedEntity,
   if (aMessage->movedAttribute())
     anOriginalPosition = pointCoordinates(aMessage->movedAttribute());
   else
-    anOriginalPosition = middlePoint(aMessage->movedObject());
+    anOriginalPosition = middlePoint(aMessage->movedObject(), this);
 
   if (!anOriginalPosition)
     return; // something has gone wrong, do not process movement
@@ -1395,5 +1513,7 @@ void SketchAPI_Sketch::dump(ModelHighAPI_Dumper& theDumper) const
       theDumper << *aFIt;
     }
     theDumper << "\n" << aSpaceShift << " ])" << std::endl;
+    // call model.do() for correct update of the document's labels related to the changed faces
+    theDumper << "model.do()" << std::endl;
   }
 }
index a6ce997207990eed4572ad8ec1110b5d09385546..e8a2b089f1136b92fbe99f3633a4cdc4a19bc3fe 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <ModelHighAPI_Interface.h>
 #include <ModelHighAPI_Macro.h>
+#include <ModelHighAPI_Selection.h>
 //--------------------------------------------------------------------------------------
 class ModelAPI_CompositeFeature;
 class ModelAPI_Object;
@@ -37,7 +38,6 @@ class ModelHighAPI_Double;
 class ModelHighAPI_Integer;
 class ModelHighAPI_RefAttr;
 class ModelHighAPI_Reference;
-class ModelHighAPI_Selection;
 class SketchAPI_Arc;
 class SketchAPI_MacroArc;
 class SketchAPI_Circle;
@@ -46,6 +46,7 @@ class SketchAPI_Ellipse;
 class SketchAPI_MacroEllipse;
 class SketchAPI_EllipticArc;
 class SketchAPI_MacroEllipticArc;
+class SketchAPI_BSpline;
 class SketchAPI_IntersectionPoint;
 class SketchAPI_Line;
 class SketchAPI_Mirror;
@@ -55,6 +56,8 @@ class SketchAPI_Rectangle;
 class SketchAPI_Rotation;
 class SketchAPI_Translation;
 //--------------------------------------------------------------------------------------
+typedef std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr> PointOrReference;
+//--------------------------------------------------------------------------------------
 /**\class SketchAPI_Sketch
  * \ingroup CPPHighAPI
  * \brief Interface for Sketch feature
@@ -290,9 +293,9 @@ public:
   /// Add ellipse
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_MacroEllipse> addEllipse(
-      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& thePoint1,
-      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& thePoint2,
-      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& thePassedPoint,
+      const PointOrReference& thePoint1,
+      const PointOrReference& thePoint2,
+      const PointOrReference& thePassedPoint,
       bool isPoint1Center = true);
   /// Add ellipse
   SKETCHAPI_EXPORT
@@ -312,10 +315,10 @@ public:
   /// Add elliptic arc
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_MacroEllipticArc> addEllipticArc(
-      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theCenter,
-      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theMajorAxisPoint,
-      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theStartPoint,
-      const std::pair<std::shared_ptr<GeomAPI_Pnt2d>, ModelHighAPI_RefAttr>& theEndPoint,
+      const PointOrReference& theCenter,
+      const PointOrReference& theMajorAxisPoint,
+      const PointOrReference& theStartPoint,
+      const PointOrReference& theEndPoint,
       bool theInversed = false);
   /// Add elliptic arc
   SKETCHAPI_EXPORT
@@ -324,6 +327,17 @@ public:
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_EllipticArc> addEllipticArc(const std::string & theExternalName);
 
+  /// Add B-spline
+  SKETCHAPI_EXPORT
+  std::shared_ptr<SketchAPI_BSpline> addSpline(
+      const ModelHighAPI_Selection & external = ModelHighAPI_Selection(),
+      const int degree = -1,
+      const std::list<PointOrReference>& poles = std::list<PointOrReference>(),
+      const std::list<ModelHighAPI_Double>& weights = std::list<ModelHighAPI_Double>(),
+      const std::list<ModelHighAPI_Double>& knots = std::list<ModelHighAPI_Double>(),
+      const std::list<ModelHighAPI_Integer>& multiplicities = std::list<ModelHighAPI_Integer>(),
+      const bool periodic = false);
+
   /// Add projection
   SKETCHAPI_EXPORT
   std::shared_ptr<SketchAPI_Projection> addProjection(
@@ -377,7 +391,8 @@ public:
   std::shared_ptr<ModelHighAPI_Interface> setAngle(
       const ModelHighAPI_RefAttr & theLine1,
       const ModelHighAPI_RefAttr & theLine2,
-      const ModelHighAPI_Double & theValue);
+      const ModelHighAPI_Double & theValue,
+      const std::string& type = std::string());
 
   /// Set complementary angle
   SKETCHAPI_EXPORT
index 467e04349007f9719eb375542bf144417d93f5ea..189c1f6e080b56c7115540188ce317b75ee18bd4 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "SketchAPI_SketchEntity.h"
 #include <SketchAPI_Arc.h>
+#include <SketchAPI_BSpline.h>
 #include <SketchAPI_Circle.h>
 #include <SketchAPI_Ellipse.h>
 #include <SketchAPI_EllipticArc.h>
@@ -30,6 +31,7 @@
 #include <ModelHighAPI_Tools.h>
 
 #include <SketchPlugin_Arc.h>
+#include <SketchPlugin_BSpline.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_Ellipse.h>
 #include <SketchPlugin_IntersectionPoint.h>
@@ -84,7 +86,8 @@ bool SketchAPI_SketchEntity::isCopy() const
 {
   // check the feature is a copy of another entity
   AttributeBooleanPtr isCopy = feature()->boolean(SketchPlugin_SketchEntity::COPY_ID());
-  return isCopy.get() && isCopy->value();
+  AttributeReferencePtr hasParent = feature()->reference(SketchPlugin_SketchEntity::PARENT_ID());
+  return (isCopy.get() && isCopy->value()) || (hasParent && hasParent->value());
 }
 
 std::list<std::shared_ptr<SketchAPI_SketchEntity> >
@@ -103,6 +106,12 @@ SketchAPI_SketchEntity::wrap(const std::list<std::shared_ptr<ModelAPI_Feature> >
       aResult.push_back(std::shared_ptr<SketchAPI_SketchEntity>(new SketchAPI_Ellipse(*anIt)));
     else if ((*anIt)->getKind() == SketchPlugin_EllipticArc::ID())
       aResult.push_back(std::shared_ptr<SketchAPI_SketchEntity>(new SketchAPI_EllipticArc(*anIt)));
+    else if ((*anIt)->getKind() == SketchPlugin_BSpline::ID())
+      aResult.push_back(std::shared_ptr<SketchAPI_SketchEntity>(new SketchAPI_BSpline(*anIt)));
+    else if ((*anIt)->getKind() == SketchPlugin_BSplinePeriodic::ID()) {
+      aResult.push_back(
+          std::shared_ptr<SketchAPI_SketchEntity>(new SketchAPI_BSplinePeriodic(*anIt)));
+    }
     else if ((*anIt)->getKind() == SketchPlugin_Point::ID())
       aResult.push_back(std::shared_ptr<SketchAPI_SketchEntity>(new SketchAPI_Point(*anIt)));
     else if ((*anIt)->getKind() == SketchPlugin_IntersectionPoint::ID())
index 93788350bae92690c04502d068eb60227b3b250c..28370b70ce25ebc7c50275432d0d4c4d194e2abb 100644 (file)
@@ -31,6 +31,7 @@
   #include "SketchAPI_MacroEllipse.h"
   #include "SketchAPI_EllipticArc.h"
   #include "SketchAPI_MacroEllipticArc.h"
+  #include "SketchAPI_BSpline.h"
   #include "SketchAPI_Constraint.h"
   #include "SketchAPI_ConstraintAngle.h"
   #include "SketchAPI_IntersectionPoint.h"
index c660b7aeb3ffad83dbcdf0577fd387857bb26390..c6e85fba300e5e60c16cee2521294998c538cb40 100644 (file)
@@ -23,6 +23,9 @@ INCLUDE(UnitTest)
 SET(PROJECT_HEADERS
     SketchPlugin.h
     SketchPlugin_Arc.h
+    SketchPlugin_BSpline.h
+    SketchPlugin_BSplineBase.h
+    SketchPlugin_BSplinePeriodic.h
     SketchPlugin_Circle.h
     SketchPlugin_Constraint.h
     SketchPlugin_ConstraintAngle.h
@@ -54,6 +57,7 @@ SET(PROJECT_HEADERS
     SketchPlugin_Line.h
     SketchPlugin_MacroArc.h
     SketchPlugin_MacroArcReentrantMessage.h
+    SketchPlugin_MacroBSpline.h
     SketchPlugin_MacroCircle.h
     SketchPlugin_MacroEllipse.h
     SketchPlugin_MacroEllipticArc.h
@@ -63,16 +67,19 @@ SET(PROJECT_HEADERS
     SketchPlugin_Point.h
     SketchPlugin_Projection.h
     SketchPlugin_Sketch.h
+    SketchPlugin_SketchDrawer.h
     SketchPlugin_SketchEntity.h
     SketchPlugin_Split.h
     SketchPlugin_Tools.h
     SketchPlugin_Trim.h
     SketchPlugin_Validators.h
-    SketchPlugin_SketchDrawer.h
 )
 
 SET(PROJECT_SOURCES
     SketchPlugin_Arc.cpp
+    SketchPlugin_BSpline.cpp
+    SketchPlugin_BSplineBase.cpp
+    SketchPlugin_BSplinePeriodic.cpp
     SketchPlugin_Circle.cpp
     SketchPlugin_Constraint.cpp
     SketchPlugin_ConstraintAngle.cpp
@@ -102,6 +109,7 @@ SET(PROJECT_SOURCES
     SketchPlugin_IntersectionPoint.cpp
     SketchPlugin_Line.cpp
     SketchPlugin_MacroArc.cpp
+    SketchPlugin_MacroBSpline.cpp
     SketchPlugin_MacroCircle.cpp
     SketchPlugin_MacroEllipse.cpp
     SketchPlugin_MacroEllipticArc.cpp
@@ -111,12 +119,12 @@ SET(PROJECT_SOURCES
     SketchPlugin_Point.cpp
     SketchPlugin_Projection.cpp
     SketchPlugin_Sketch.cpp
+    SketchPlugin_SketchDrawer.cpp
     SketchPlugin_SketchEntity.cpp
     SketchPlugin_Split.cpp
     SketchPlugin_Tools.cpp
     SketchPlugin_Trim.cpp
     SketchPlugin_Validators.cpp
-    SketchPlugin_SketchDrawer.cpp
 )
 
 SET(PROJECT_LIBRARIES
@@ -125,6 +133,7 @@ SET(PROJECT_LIBRARIES
     GeomAlgoAPI
     ModelAPI
     ModelGeomAlgo
+    ModuleBase
     SketcherPrs
     GeomDataAPI
 )
@@ -135,14 +144,18 @@ SET(XML_RESOURCES
 )
 
 SET(TEXT_RESOURCES
-       SketchPlugin_msg_en.ts
-       SketchPlugin_msg_fr.ts
+    SketchPlugin_msg_en.ts
+    SketchPlugin_msg_fr.ts
 )
 
+# sources / moc wrappings
+QT_WRAP_MOC(PROJECT_AUTOMOC ${PROJECT_MOC_HEADERS})
+
+SOURCE_GROUP ("Generated Files" FILES ${PROJECT_AUTOMOC})
 SOURCE_GROUP ("Resource Files" FILES ${TEXT_RESOURCES})
 
 ADD_DEFINITIONS(-DSKETCHPLUGIN_EXPORTS)
-ADD_LIBRARY(SketchPlugin MODULE ${PROJECT_SOURCES} ${PROJECT_HEADERS} ${XML_RESOURCES} ${TEXT_RESOURCES})
+ADD_LIBRARY(SketchPlugin MODULE ${PROJECT_SOURCES} ${PROJECT_HEADERS} ${XML_RESOURCES} ${TEXT_RESOURCES} ${PROJECT_AUTOMOC})
 TARGET_LINK_LIBRARIES(SketchPlugin ${PROJECT_LIBRARIES})
 
 INCLUDE_DIRECTORIES(
@@ -150,10 +163,12 @@ INCLUDE_DIRECTORIES(
   ../Events
   ../ModelAPI
   ../ModelGeomAlgo
+  ../ModuleBase
   ../GeomAPI
   ../GeomAlgoAPI
   ../GeomDataAPI
   ../SketcherPrs
+  ${OpenCASCADE_INCLUDE_DIR}
 )
 
 INSTALL(TARGETS SketchPlugin DESTINATION ${SHAPER_INSTALL_PLUGIN_FILES})
@@ -205,14 +220,26 @@ ADD_UNIT_TESTS(
   Test3019.py
   Test3087_1.py
   Test3087_2.py
+  Test3132.py
   TestArcBehavior.py
+  TestBSplineAddPole.py
   TestChangeSketchPlane1.py
   TestChangeSketchPlane2.py
   TestChangeSketchPlane3.py
   TestChangeSketchPlane4.py
   TestConstraintAngle.py
+  TestConstraintAngle_v0_1.py
+  TestConstraintAngle_v0_2.py
+  TestConstraintAngle_v20191210_1.py
+  TestConstraintAngle_v20191210_2.py
+  TestConstraintAngleBehaviorDirect.py
+  TestConstraintAngleBehaviorSupplementary_1.py
+  TestConstraintAngleBehaviorSupplementary_2.py
+  TestConstraintAngleBehaviorBackward_1.py
+  TestConstraintAngleBehaviorBackward_2.py
   TestConstraintAngleEllipse.py
   TestConstraintCoincidence.py
+  TestConstraintCoincidenceBSpline.py
   TestConstraintCoincidenceEllipse.py
   TestConstraintCoincidenceEllipticArc.py
   TestConstraintCollinear.py
@@ -241,6 +268,7 @@ ADD_UNIT_TESTS(
   TestConstraintRadius.py
   TestConstraintRadiusFailure.py
   TestConstraintTangent.py
+  TestConstraintTangentBSpline.py
   TestConstraintTangentEllipse.py
   TestConstraintTangentEllipticArc.py
   TestConstraintVertical.py
@@ -249,6 +277,8 @@ ADD_UNIT_TESTS(
   TestCreateArcByThreePoints.py
   TestCreateArcByTransversalLine.py
   TestCreateArcChangeType.py
+  TestCreateBSpline.py
+  TestCreateBSplinePeriodic.py
   TestCreateCircleByCenterAndPassed.py
   TestCreateCircleByThreePoints.py
   TestCreateCircleChangeType.py
@@ -257,6 +287,7 @@ ADD_UNIT_TESTS(
   TestCreateEllipseByExternal.py
   TestCreateEllipticArc.py
   TestCreateEllipticArcByExternal.py
+  TestCreateMacroBSpline.py
   TestDegeneratedGeometry.py
   TestDistanceDump.py
   TestDistanceSignedVsUnsigned01.py
@@ -284,12 +315,16 @@ ADD_UNIT_TESTS(
   TestMultiTranslation.py
   TestPresentation.py
   TestProjection.py
+  TestProjectionBSpline.py
+  TestProjectionBSplinePeriodic.py
   TestProjectionEllipse.py
   TestProjectionEllipticArc.py
   TestProjectionIntoResult.py
   TestProjectionUpdate.py
   TestRectangle.py
   TestRemainingDoF.py
+  TestRemoveBSpline.py
+  TestRemoveBSplinePeriodic.py
   TestRemoveEllipse.py
   TestRemoveEllipticArc.py
   TestRemoveSketch.py
@@ -327,6 +362,8 @@ ADD_UNIT_TESTS(
 if(${SKETCHER_CHANGE_RADIUS_WHEN_MOVE})
   ADD_UNIT_TESTS(
     TestMoveArc.py
+    TestMoveBSpline.py
+    TestMoveBSplinePeriodic.py
     TestMoveCircle.py
     TestMoveEllipse.py
     TestMoveEllipticArc.py
diff --git a/src/SketchPlugin/SketchPlugin_BSpline.cpp b/src/SketchPlugin/SketchPlugin_BSpline.cpp
new file mode 100644 (file)
index 0000000..0183573
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <SketchPlugin_BSpline.h>
+
+#include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
+
+
+SketchPlugin_BSpline::SketchPlugin_BSpline()
+  : SketchPlugin_BSplineBase()
+{
+}
+
+void SketchPlugin_BSpline::initDerivedClassAttributes()
+{
+  data()->addAttribute(START_ID(), GeomDataAPI_Point2D::typeId());
+  data()->addAttribute(END_ID(), GeomDataAPI_Point2D::typeId());
+
+  SketchPlugin_BSplineBase::initDerivedClassAttributes();
+}
+
+void SketchPlugin_BSpline::attributeChanged(const std::string& theID) {
+  if (theID == POLES_ID()) {
+    AttributePoint2DArrayPtr aPolesArray =
+        std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        attribute(START_ID()))->setValue(aPolesArray->pnt(0));
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        attribute(END_ID()))->setValue(aPolesArray->pnt(aPolesArray->size() - 1));
+  }
+  else
+    SketchPlugin_BSplineBase::attributeChanged(theID);
+}
diff --git a/src/SketchPlugin/SketchPlugin_BSpline.h b/src/SketchPlugin/SketchPlugin_BSpline.h
new file mode 100644 (file)
index 0000000..ff27cd3
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketchPlugin_BSpline_H_
+#define SketchPlugin_BSpline_H_
+
+#include <SketchPlugin_BSplineBase.h>
+
+/**\class SketchPlugin_BSpline
+ * \ingroup Plugins
+ * \brief Feature for creation of the B-spline curve in the sketch.
+ */
+class SketchPlugin_BSpline : public SketchPlugin_BSplineBase
+{
+public:
+  /// B-spline feature kind
+  inline static const std::string& ID()
+  {
+    static const std::string ID("SketchBSpline");
+    return ID;
+  }
+
+  /// start point of B-spline curve
+  inline static const std::string& START_ID()
+  {
+    static const std::string ID("start_point");
+    return ID;
+  }
+  /// end point of B-spline curve
+  inline static const std::string& END_ID()
+  {
+    static const std::string ID("end_point");
+    return ID;
+  }
+
+  /// Returns the kind of a feature
+  SKETCHPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = SketchPlugin_BSpline::ID();
+    return MY_KIND;
+  }
+
+  /// Called on change of any argument-attribute of this object
+  SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
+
+  /// Use plugin manager for features creation
+  SketchPlugin_BSpline();
+
+protected:
+  /// \brief Initializes attributes of derived class.
+  virtual void initDerivedClassAttributes();
+
+  virtual bool isPeriodic() const { return false; }
+};
+
+#endif
diff --git a/src/SketchPlugin/SketchPlugin_BSplineBase.cpp b/src/SketchPlugin/SketchPlugin_BSplineBase.cpp
new file mode 100644 (file)
index 0000000..a663675
--- /dev/null
@@ -0,0 +1,260 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <SketchPlugin_BSplineBase.h>
+
+#include <SketchPlugin_ConstraintCoincidenceInternal.h>
+#include <SketchPlugin_Line.h>
+#include <SketchPlugin_MacroBSpline.h>
+#include <SketchPlugin_Point.h>
+#include <SketchPlugin_Sketch.h>
+
+#include <Events_InfoMessage.h>
+
+#include <GeomAlgoAPI_EdgeBuilder.h>
+
+#include <GeomAPI_BSpline2d.h>
+#include <GeomAPI_Pnt2d.h>
+
+#include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeIntArray.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_ResultConstruction.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Validator.h>
+
+
+SketchPlugin_BSplineBase::SketchPlugin_BSplineBase()
+  : SketchPlugin_SketchEntity()
+{
+}
+
+void SketchPlugin_BSplineBase::initDerivedClassAttributes()
+{
+  data()->addAttribute(POLES_ID(), GeomDataAPI_Point2DArray::typeId());
+  data()->addAttribute(WEIGHTS_ID(), ModelAPI_AttributeDoubleArray::typeId());
+  data()->addAttribute(KNOTS_ID(), ModelAPI_AttributeDoubleArray::typeId());
+  data()->addAttribute(MULTS_ID(), ModelAPI_AttributeIntArray::typeId());
+  data()->addAttribute(DEGREE_ID(), ModelAPI_AttributeInteger::typeId());
+
+  data()->addAttribute(EXTERNAL_ID(), ModelAPI_AttributeSelection::typeId());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), EXTERNAL_ID());
+}
+
+void SketchPlugin_BSplineBase::execute()
+{
+  SketchPlugin_Sketch* aSketch = sketch();
+  if(!aSketch) {
+    return;
+  }
+
+  AttributePoint2DArrayPtr aPolesArray =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
+  AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID());
+  AttributeDoubleArrayPtr aKnotsArray = data()->realArray(KNOTS_ID());
+  AttributeIntArrayPtr aMultsArray = data()->intArray(MULTS_ID());
+  AttributeIntegerPtr aDegreeAttr = data()->integer(DEGREE_ID());
+
+  // collect poles
+  std::list<GeomPnt2dPtr> aPoles2D;
+  for (int anIndex = 0; anIndex < aPolesArray->size(); ++anIndex) {
+    GeomPnt2dPtr aPole = aPolesArray->pnt(anIndex);
+    aPoles2D.push_back(aPole);
+  }
+  // collect weights
+  std::list<double> aWeights;
+  for (int anIndex = 0; anIndex < aWeightsArray->size(); ++anIndex)
+    aWeights.push_back(aWeightsArray->value(anIndex));
+  // collect knots
+  std::list<double> aKnots;
+  for (int anIndex = 0; anIndex < aKnotsArray->size(); ++anIndex)
+    aKnots.push_back(aKnotsArray->value(anIndex));
+  // collect multiplicities
+  std::list<int> aMults;
+  for (int anIndex = 0; anIndex < aMultsArray->size(); ++anIndex)
+    aMults.push_back(aMultsArray->value(anIndex));
+
+  // create result B-spline curve
+  GeomShapePtr anEdge = GeomAlgoAPI_EdgeBuilder::bsplineOnPlane(aSketch->coordinatePlane(),
+      aPoles2D, aWeights, aKnots, aMults, aDegreeAttr->value(), isPeriodic());
+
+  ResultConstructionPtr aResult = document()->createConstruction(data(), 0);
+  aResult->setShape(anEdge);
+  aResult->setIsInHistory(false);
+  setResult(aResult, 0);
+}
+
+bool SketchPlugin_BSplineBase::isFixed() {
+  return data()->selection(EXTERNAL_ID())->context().get() != NULL;
+}
+
+void SketchPlugin_BSplineBase::attributeChanged(const std::string& theID) {
+}
+
+bool SketchPlugin_BSplineBase::customAction(const std::string& theActionId)
+{
+  // parse for the action and an index divided by '#'
+  std::string anAction;
+  int anIndex = -1;
+  size_t pos = theActionId.find('#');
+  if (pos != std::string::npos) {
+    anAction = theActionId.substr(0, pos);
+    anIndex = std::stoi(theActionId.substr(pos + 1));
+  }
+
+  if (anAction == ADD_POLE_ACTION_ID()) {
+    return addPole(anIndex);
+  }
+
+  std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
+  Events_InfoMessage("SketchPlugin_BSplineBase", aMsg).arg(getKind()).arg(theActionId).send();
+  return false;
+}
+
+bool SketchPlugin_BSplineBase::addPole(const int theAfter)
+{
+  AttributePoint2DArrayPtr aPolesArray =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
+  AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID());
+
+  int anAfter = (!isPeriodic() && theAfter == aPolesArray->size() - 1) ? theAfter - 1 : theAfter;
+
+  // find internal coincidences applied to the poles with greater indices
+  std::list<AttributeIntegerPtr> aCoincidentPoleIndex;
+  std::map<int, FeaturePtr> aControlPoles, aControlSegments;
+  bool hasAuxSegment = false;
+  const std::set<AttributePtr>& aRefs = data()->refsToMe();
+  for (std::set<AttributePtr>::iterator anIt = aRefs.begin(); anIt != aRefs.end(); ++anIt) {
+    FeaturePtr aFeature = ModelAPI_Feature::feature((*anIt)->owner());
+    if (aFeature->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
+      AttributeIntegerPtr anIndex;
+      AttributeRefAttrPtr aNonSplinePoint;
+      if ((*anIt)->id() == SketchPlugin_ConstraintCoincidenceInternal::ENTITY_A()) {
+        anIndex = aFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A());
+        aNonSplinePoint = aFeature->refattr(SketchPlugin_Constraint::ENTITY_B());
+      }
+      else if ((*anIt)->id() == SketchPlugin_ConstraintCoincidenceInternal::ENTITY_B()) {
+        anIndex = aFeature->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
+        aNonSplinePoint = aFeature->refattr(SketchPlugin_Constraint::ENTITY_A());
+      }
+
+      if (anIndex && anIndex->isInitialized()) {
+        if (anIndex->value() > anAfter) {
+          aCoincidentPoleIndex.push_back(anIndex);
+          FeaturePtr aParent = ModelAPI_Feature::feature(aNonSplinePoint->attr()->owner());
+          if (aParent->getKind() == SketchPlugin_Point::ID())
+            aControlPoles[anIndex->value()] = aParent;
+          else if (aParent->getKind() == SketchPlugin_Line::ID() &&
+                   aNonSplinePoint->attr()->id() == SketchPlugin_Line::START_ID())
+            aControlSegments[anIndex->value()] = aParent;
+        }
+        else if (anIndex->value() == anAfter && !hasAuxSegment) {
+          // check the constrained object is a segment of the control polygon
+          if (aNonSplinePoint && !aNonSplinePoint->isObject() &&
+              aNonSplinePoint->attr()->id() == SketchPlugin_Line::START_ID()) {
+            hasAuxSegment = true;
+            aCoincidentPoleIndex.push_back(anIndex);
+            aControlSegments[anIndex->value()] =
+                ModelAPI_Feature::feature(aNonSplinePoint->attr()->owner());
+          }
+        }
+      }
+    }
+  }
+
+  bool aWasBlocked = data()->blockSendAttributeUpdated(true);
+
+  // add new pole and default weight
+  std::list<GeomPnt2dPtr> aPoles;
+  aPolesArray->setSize(aPolesArray->size() + 1);
+  aPolesArray->setPnt(aPolesArray->size() - 1, aPolesArray->pnt(0)); // for periodic spline
+  for (int i = aPolesArray->size() - 2; i > anAfter; --i) {
+    aPoles.push_front(aPolesArray->pnt(i));
+    aPolesArray->setPnt(i + 1, aPoles.front());
+  }
+
+  GeomPnt2dPtr aCurPole = aPolesArray->pnt(anAfter);
+  GeomPnt2dPtr aNextPole = aPolesArray->pnt(anAfter + 1);
+  aPolesArray->setPnt(anAfter + 1, (aCurPole->x() + aNextPole->x()) * 0.5,
+                                   (aCurPole->y() + aNextPole->y()) * 0.5);
+  for (int i = anAfter + 1; i >= 0; --i)
+    aPoles.push_front(aPolesArray->pnt(i));
+
+  std::list<double> aWeights;
+  for (int i = 0; i < aWeightsArray->size(); ++i) {
+    aWeights.push_back(aWeightsArray->value(i));
+    if (i == anAfter)
+      aWeights.push_back(1.0); // default weight
+  }
+  aWeightsArray->setSize(aWeightsArray->size() + 1);
+  std::list<double>::iterator aWIt = aWeights.begin();
+  for (int i = 0; i < aWeightsArray->size(); ++i, ++aWIt)
+    aWeightsArray->setValue(i, *aWIt);
+
+  // recalculate knots and multiplicities
+  std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve(
+      new GeomAPI_BSpline2d(aPoles, aWeights, isPeriodic()));
+
+  integer(DEGREE_ID())->setValue(aBSplineCurve->degree());
+
+  AttributeDoubleArrayPtr aKnotsAttr = data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID());
+  std::list<double> aKnots = aBSplineCurve->knots();
+  int aSize = (int)aKnots.size();
+  aKnotsAttr->setSize(aSize);
+  std::list<double>::iterator aKIt = aKnots.begin();
+  for (int index = 0; index < aSize; ++index, ++aKIt)
+    aKnotsAttr->setValue(index, *aKIt);
+
+  AttributeIntArrayPtr aMultsAttr = data()->intArray(SketchPlugin_BSplineBase::MULTS_ID());
+  std::list<int> aMults = aBSplineCurve->mults();
+  aSize = (int)aMults.size();
+  aMultsAttr->setSize(aSize);
+  std::list<int>::iterator aMIt = aMults.begin();
+  for (int index = 0; index < aSize; ++index, ++aMIt)
+    aMultsAttr->setValue(index, *aMIt);
+
+  data()->blockSendAttributeUpdated(aWasBlocked, true);
+
+  // update indices of internal coincidences
+  for (std::list<AttributeIntegerPtr>::iterator aCIt = aCoincidentPoleIndex.begin();
+       aCIt != aCoincidentPoleIndex.end(); ++aCIt)
+    (*aCIt)->setValue((*aCIt)->value() + 1);
+
+  // create auxiliary segment and pole updating the control polygon
+  SketchPlugin_MacroBSpline::createAuxiliaryPole(aPolesArray, anAfter + 1);
+  if (hasAuxSegment)
+    SketchPlugin_MacroBSpline::createAuxiliarySegment(aPolesArray, anAfter, anAfter + 1);
+
+  // update names of features representing control polygon
+  for (std::map<int, FeaturePtr>::iterator anIt = aControlPoles.begin();
+       anIt != aControlPoles.end(); ++anIt) {
+    SketchPlugin_MacroBSpline::assignDefaultNameForAux(anIt->second, aPolesArray, anIt->first + 1);
+  }
+  for (std::map<int, FeaturePtr>::iterator anIt = aControlSegments.begin();
+       anIt != aControlSegments.end(); ++anIt) {
+    SketchPlugin_MacroBSpline::assignDefaultNameForAux(anIt->second, aPolesArray,
+        anIt->first + 1, (anIt->first + 2) % aPolesArray->size());
+  }
+
+  return true;
+}
diff --git a/src/SketchPlugin/SketchPlugin_BSplineBase.h b/src/SketchPlugin/SketchPlugin_BSplineBase.h
new file mode 100644 (file)
index 0000000..fc75396
--- /dev/null
@@ -0,0 +1,103 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketchPlugin_BSplineBase_H_
+#define SketchPlugin_BSplineBase_H_
+
+#include <SketchPlugin.h>
+#include <SketchPlugin_SketchEntity.h>
+
+/**\class SketchPlugin_BSplineBase
+ * \ingroup Plugins
+ * \brief Base class for B-spline curves in the sketch.
+ */
+class SketchPlugin_BSplineBase : public SketchPlugin_SketchEntity
+{
+public:
+  /// list of B-spline poles
+  inline static const std::string& POLES_ID()
+  {
+    static const std::string ID("poles");
+    return ID;
+  }
+
+  /// list of B-spline weights
+  inline static const std::string& WEIGHTS_ID()
+  {
+    static const std::string ID("weights");
+    return ID;
+  }
+
+  /// attribute to store the degree of B-spline
+  inline static const std::string& DEGREE_ID()
+  {
+    static const std::string ID("degree");
+    return ID;
+  }
+
+  /// list of B-spline knots
+  inline static const std::string& KNOTS_ID()
+  {
+    static const std::string ID("knots");
+    return ID;
+  }
+
+  /// list of B-spline multiplicities
+  inline static const std::string& MULTS_ID()
+  {
+    static const std::string ID("multiplicities");
+    return ID;
+  }
+
+  /// name for add pole action
+  inline static const std::string& ADD_POLE_ACTION_ID()
+  {
+    static const std::string ID("AddPole");
+    return ID;
+  }
+
+  /// Returns true is sketch element is under the rigid constraint
+  SKETCHPLUGIN_EXPORT virtual bool isFixed();
+
+  /// Called on change of any argument-attribute of this object
+  SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
+
+  /// Creates a new part document if needed
+  SKETCHPLUGIN_EXPORT virtual void execute();
+
+  /// Updates the B-spline curve.
+  /// \param[in] theActionId action key id (in following form: Action#Index)
+  /// \return \c false in case the action not performed.
+  SKETCHPLUGIN_EXPORT virtual bool customAction(const std::string& theActionId);
+
+protected:
+  /// Called from the derived class
+  SketchPlugin_BSplineBase();
+
+  /// \brief Initializes attributes of derived class.
+  virtual void initDerivedClassAttributes();
+
+  /// \brief Return \c true if the B-spline curve is periodic
+  virtual bool isPeriodic() const = 0;
+
+  /// Add new pole after the pole with the given index
+  bool addPole(const int theAfter);
+};
+
+#endif
diff --git a/src/SketchPlugin/SketchPlugin_BSplinePeriodic.cpp b/src/SketchPlugin/SketchPlugin_BSplinePeriodic.cpp
new file mode 100644 (file)
index 0000000..eb5fa74
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <SketchPlugin_BSplinePeriodic.h>
+
+SketchPlugin_BSplinePeriodic::SketchPlugin_BSplinePeriodic()
+  : SketchPlugin_BSplineBase()
+{
+}
diff --git a/src/SketchPlugin/SketchPlugin_BSplinePeriodic.h b/src/SketchPlugin/SketchPlugin_BSplinePeriodic.h
new file mode 100644 (file)
index 0000000..b5b4e62
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketchPlugin_BSplinePeriodic_H_
+#define SketchPlugin_BSplinePeriodic_H_
+
+#include <SketchPlugin_BSplineBase.h>
+
+/**\class SketchPlugin_BSplinePeriodic
+ * \ingroup Plugins
+ * \brief Feature for creation of the periodic B-spline curve in the sketch.
+ */
+class SketchPlugin_BSplinePeriodic : public SketchPlugin_BSplineBase
+{
+public:
+  /// B-spline feature kind
+  inline static const std::string& ID()
+  {
+    static const std::string ID("SketchBSplinePeriodic");
+    return ID;
+  }
+
+  /// Returns the kind of a feature
+  SKETCHPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = SketchPlugin_BSplinePeriodic::ID();
+    return MY_KIND;
+  }
+
+  /// Use plugin manager for features creation
+  SketchPlugin_BSplinePeriodic();
+
+protected:
+  virtual bool isPeriodic() const { return true; }
+};
+
+#endif
index bf2f211d96b99f99ce15e407a27ef176135e3147..861f37731cb591f66172cf88643d08a2c0390edb 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_EventReentrantMessage.h>
 #include <ModelAPI_Session.h>
 #include <ModelAPI_Validator.h>
 
@@ -72,6 +73,12 @@ void SketchPlugin_ConstraintAngle::initAttributes()
   data()->addAttribute(LOCATION_TYPE_ID(), ModelAPI_AttributeInteger::typeId());
   aValidators->registerNotObligatory(getKind(), LOCATION_TYPE_ID());
 
+  data()->addAttribute(PREV_TYPE_ID(), ModelAPI_AttributeInteger::typeId());
+  data()->attribute(PREV_TYPE_ID())->setIsArgument(false);
+  aValidators->registerNotObligatory(getKind(), PREV_TYPE_ID());
+  if (attribute(TYPE_ID())->isInitialized())
+    integer(PREV_TYPE_ID())->setValue(integer(TYPE_ID())->value());
+
   data()->addAttribute(SELECTED_FIRST_POINT_ID(), GeomDataAPI_Point2D::typeId());
   data()->attribute(SELECTED_FIRST_POINT_ID())->setIsArgument(false);
   aValidators->registerNotObligatory(getKind(), SELECTED_FIRST_POINT_ID());
@@ -80,10 +87,15 @@ void SketchPlugin_ConstraintAngle::initAttributes()
   data()->attribute(SELECTED_SECOND_POINT_ID())->setIsArgument(false);
   aValidators->registerNotObligatory(getKind(), SELECTED_SECOND_POINT_ID());
 
-  if (attribute(TYPE_ID())->isInitialized())
-    myPrevAngleType = integer(TYPE_ID())->value();
-  else
-    myPrevAngleType = (int)SketcherPrs_Tools::ANGLE_DIRECT;
+  AttributeIntegerPtr aVerAttr = std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(
+      data()->addAttribute(VERSION_ID(), ModelAPI_AttributeInteger::typeId()));
+  aVerAttr->setIsArgument(false);
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), VERSION_ID());
+  if (!aVerAttr->isInitialized()) {
+    // this is a newly created feature (not read from file),
+    // so, initialize the latest version
+    aVerAttr->setValue(THE_VERSION_1);
+  }
 }
 
 void SketchPlugin_ConstraintAngle::colorConfigInfo(std::string& theSection, std::string& theName,
@@ -103,6 +115,10 @@ void SketchPlugin_ConstraintAngle::execute()
   if (!anAttrA->isInitialized() || !anAttrB->isInitialized())
     return;
 
+  AttributeIntegerPtr aVersion = integer(VERSION_ID());
+  if (!aVersion->isInitialized() || aVersion->value() < THE_VERSION_1)
+    updateVersion();
+
   AttributeDoublePtr anAttrValue = real(ANGLE_VALUE_ID());
   if (!anAttrValue->isInitialized())
     calculateAngle();
@@ -129,8 +145,29 @@ AISObjectPtr SketchPlugin_ConstraintAngle::getAISObject(AISObjectPtr thePrevious
   return anAIS;
 }
 
+// LCOV_EXCL_START
+std::string SketchPlugin_ConstraintAngle::processEvent(
+    const std::shared_ptr<Events_Message>& theMessage)
+{
+  std::string aFilledAttributeName;
+
+  std::shared_ptr<ModelAPI_EventReentrantMessage> aReentrantMessage =
+      std::dynamic_pointer_cast<ModelAPI_EventReentrantMessage>(theMessage);
+  if (aReentrantMessage.get()) {
+    aFilledAttributeName = ENTITY_A();
+    refattr(ENTITY_A())->setObject(aReentrantMessage->selectedObject());
+    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(SELECTED_FIRST_POINT_ID()))
+        ->setValue(aReentrantMessage->clickedPoint());
+  }
+  return aFilledAttributeName;
+}
+// LCOV_EXCL_STOP
+
 void SketchPlugin_ConstraintAngle::attributeChanged(const std::string& theID)
 {
+  if (myFlyoutUpdate)
+    return;
+
   std::shared_ptr<ModelAPI_Data> aData = data();
   if (!aData)
     return;
@@ -143,28 +180,15 @@ void SketchPlugin_ConstraintAngle::attributeChanged(const std::string& theID)
   if (!aLineA || !aLineB)
     return;
 
+  AttributeIntegerPtr aVersion = integer(VERSION_ID());
+  if (!aVersion->isInitialized() || aVersion->value() < THE_VERSION_1)
+    updateVersion();
+
   if (theID == ENTITY_A() || theID == ENTITY_B() ||
       theID == TYPE_ID() || theID == ANGLE_VALUE_ID()) {
     calculateAngle();
-  } else if (theID == FLYOUT_VALUE_PNT() && !myFlyoutUpdate) {
-    // Recalculate flyout point in local coordinates
-    // coordinates are calculated according to the center of shapes intersection
-    std::shared_ptr<GeomDataAPI_Point2D> aFlyoutAttr =
-      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(attribute(FLYOUT_VALUE_PNT()));
-
-    std::shared_ptr<ModelAPI_Data> aData = data();
-    std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
-
-    // Intersection of lines
-    std::shared_ptr<GeomAPI_Pnt2d> anInter = intersect(aLineA, aLineB);
-    if (!anInter)
-      return;
-
-    myFlyoutUpdate = true;
-    std::shared_ptr<GeomAPI_XY> aFlyoutDir = aFlyoutAttr->pnt()->xy()->decreased(anInter->xy());
-    if (aFlyoutDir->dot(aFlyoutDir) < tolerance * tolerance)
-      aFlyoutAttr->setValue(aFlyoutAttr->x() + tolerance, aFlyoutAttr->y());
-    myFlyoutUpdate = false;
+  } else if (theID == FLYOUT_VALUE_PNT()) {
+    compute(theID);
   }
 }
 
@@ -189,16 +213,18 @@ void SketchPlugin_ConstraintAngle::calculateAngle()
   bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
   bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
 
-  std::shared_ptr<GeomAPI_Angle2d> anAng(
-      new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
-  double anAngle = anAng->angleDegree();
-
   AttributeDoublePtr anAngleValueAttr = real(ANGLE_VALUE_ID());
-  if (!anAngleValueAttr->isInitialized())
-    anAngleValueAttr->setValue(getAngleForType(fabs(anAngle)));
+  if (!anAngleValueAttr->isInitialized()) {
+    std::shared_ptr<GeomAPI_Angle2d> anAng(
+        new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
+    anAngleValueAttr->setValue(getAngleForType(fabs(anAng->angleDegree())));
+  }
+
+  std::shared_ptr<GeomAPI_Angle2d> anAng(new GeomAPI_Angle2d(aLine1, false, aLine2, false));
+  double anAngle = anAng->angleDegree();
 
   anAngle /= fabs(anAngle);
-  anAngle *= getAngleForType(anAngleValueAttr->value());
+  anAngle *= getAngleForType(anAngleValueAttr->value(), isReversed1, isReversed2);
 
   // update value of the constraint to be passed to the solver
   real(SketchPlugin_Constraint::VALUE())->setValue(anAngle);
@@ -252,6 +278,7 @@ void SketchPlugin_ConstraintAngle::calculateAnglePosition()
   boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2);
 }
 
+// Convert angle value from the DIRECT to any given type.
 static double angleForType(const double theAngle, const int theType)
 {
   double anAngle = theAngle;
@@ -271,28 +298,33 @@ static double angleForType(const double theAngle, const int theType)
   return anAngle;
 }
 
-double SketchPlugin_ConstraintAngle::getAngleForType(double theAngle)
+double SketchPlugin_ConstraintAngle::getAngleForType(double theAngle,
+                                                     bool isReversed1,
+                                                     bool isReversed2)
 {
-  return angleForType(theAngle, integer(TYPE_ID())->value());
+  double anAngle = angleForType(theAngle, integer(TYPE_ID())->value());
+  if (isReversed1 != isReversed2)
+    anAngle = 180.0 - anAngle;
+  return anAngle;
 }
 
-void SketchPlugin_ConstraintAngle::updateAngleValue()
+// Convert angle value or a text expression from one angle type to another
+static void convertAngle(AttributeDoublePtr theAngle,
+                         const int thePrevType, const int theNewType)
 {
-  AttributeIntegerPtr anAngleType = integer(TYPE_ID());
-  AttributeDoublePtr anAngleValueAttr = real(ANGLE_VALUE_ID());
-  if (anAngleValueAttr->isInitialized()) {
-    if (anAngleValueAttr->text().empty()) {
+  if (theAngle->isInitialized()) {
+    if (theAngle->text().empty()) {
       // calculate value related to the type twice:
       // the first time - to return to direct angle,
       // the second time - to apply new type
-      double aValue = angleForType(anAngleValueAttr->value(), myPrevAngleType);
-      aValue = angleForType(aValue, anAngleType->value());
-      anAngleValueAttr->setValue(aValue);
+      double aValue = angleForType(theAngle->value(), thePrevType);
+      aValue = angleForType(aValue, theNewType);
+      theAngle->setValue(aValue);
     }
     else {
       // process the parametric value
-      std::string anAngleText = anAngleValueAttr->text();
-      std::regex anAngleRegex("\\s*([-+]?[0-9]*\\.?[0-9]*)\\s*([-+])\\s*\\((.*)\\)",
+      std::string anAngleText = theAngle->text();
+      std::regex anAngleRegex("\\s*([-+]?[0-9]*\\.?[0-9]*)\\s*([-+])\\s*\\((.*)\\)$",
                               std::regex_constants::ECMAScript);
 
       double anAnglePrefix = 0.0;
@@ -306,13 +338,13 @@ void SketchPlugin_ConstraintAngle::updateAngleValue()
         anAngleText = aResult[3].str();
       }
 
-      if (myPrevAngleType != SketcherPrs_Tools::ANGLE_DIRECT)
+      if (thePrevType != SketcherPrs_Tools::ANGLE_DIRECT)
         aSignInd = 1 - aSignInd;
-      anAnglePrefix = angleForType(anAnglePrefix, myPrevAngleType);
+      anAnglePrefix = angleForType(anAnglePrefix, thePrevType);
 
-      if (anAngleType->value() != SketcherPrs_Tools::ANGLE_DIRECT)
+      if (theNewType != SketcherPrs_Tools::ANGLE_DIRECT)
         aSignInd = 1 - aSignInd;
-      anAnglePrefix = angleForType(anAnglePrefix, anAngleType->value());
+      anAnglePrefix = angleForType(anAnglePrefix, theNewType);
 
       std::ostringstream aText;
       bool isPrintSign = true;
@@ -323,10 +355,33 @@ void SketchPlugin_ConstraintAngle::updateAngleValue()
       if (isPrintSign)
         aText << " " << aSignPrefix[aSignInd] << " (";
       aText << anAngleText << (isPrintSign ? ")" : "");
-      anAngleValueAttr->setText(aText.str());
+      theAngle->setText(aText.str());
     }
   }
-  myPrevAngleType = anAngleType->value();
+}
+
+void SketchPlugin_ConstraintAngle::updateAngleValue()
+{
+  AttributeIntegerPtr anAngleType = integer(TYPE_ID());
+  AttributeIntegerPtr aPrevAngleType = integer(PREV_TYPE_ID());
+  convertAngle(real(ANGLE_VALUE_ID()), aPrevAngleType->value(), anAngleType->value());
+  aPrevAngleType->setValue(anAngleType->value());
+}
+
+static GeomPnt2dPtr lineBoundary(const FeaturePtr& theLine, const bool theReversed,
+                                 const GeomPnt2dPtr& thePointToAvoid)
+{
+  GeomPnt2dPtr aPoint = SketcherPrs_Tools::getPoint(theLine.get(),
+      theReversed ? SketchPlugin_Line::START_ID() : SketchPlugin_Line::END_ID());
+  if (aPoint->distance(thePointToAvoid) < tolerance) {
+    // extremity is equal to the intersection point,
+    // thus recalculate it using another boundary point
+    aPoint = SketcherPrs_Tools::getPoint(theLine.get(),
+        theReversed ? SketchPlugin_Line::END_ID() : SketchPlugin_Line::START_ID());
+    aPoint->setX(thePointToAvoid->x() * 2.0 - aPoint->x());
+    aPoint->setY(thePointToAvoid->y() * 2.0 - aPoint->y());
+  }
+  return aPoint;
 }
 
 bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId)
@@ -338,39 +393,133 @@ bool SketchPlugin_ConstraintAngle::compute(const std::string& theAttributeId)
 
   std::shared_ptr<GeomDataAPI_Point2D> aFlyOutAttr = std::dynamic_pointer_cast<
                            GeomDataAPI_Point2D>(attribute(theAttributeId));
-  if (aFlyOutAttr->isInitialized() &&
-      (fabs(aFlyOutAttr->x()) >= tolerance || fabs(aFlyOutAttr->y()) >= tolerance))
-    return false;
 
   DataPtr aData = data();
   std::shared_ptr<GeomAPI_Ax3> aPlane = SketchPlugin_Sketch::plane(sketch());
-  FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_A());
-  FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, SketchPlugin_Constraint::ENTITY_B());
+  FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_A());
+  FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(aData, ENTITY_B());
 
   if ((aLineA.get() == NULL) || (aLineB.get() == NULL))
     return false;
 
-  // Start and end points of lines
-  GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
-  GeomPnt2dPtr aEndA   = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
-  if (aStartA->distance(aEndA) < tolerance)
+  // Intersection of lines
+  std::shared_ptr<GeomAPI_Pnt2d> anInter = intersect(aLineA, aLineB);
+  if (!anInter)
     return false;
 
-  GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
-  GeomPnt2dPtr aEndB   = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
-  if (aStartB->distance(aEndB) < tolerance)
-    return false;
+  bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
+  bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
+
+  int anAngleType = integer(TYPE_ID())->value();
+
+  bool isSupplementary = anAngleType == (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY;
+
+  // point on lines to compose an angle
+  GeomPnt2dPtr aPointA = lineBoundary(aLineA, isReversed1 ^ isSupplementary, anInter);
+  GeomPnt2dPtr aPointB = lineBoundary(aLineB, isReversed2, anInter);
 
   myFlyoutUpdate = true;
-  double aX = (aStartA->x() + aEndA->x() + aStartB->x() + aEndB->x()) / 4.;
-  double aY = (aStartA->y() + aEndA->y() + aStartB->y() + aEndB->y()) / 4.;
+  if (aFlyOutAttr->isInitialized()) {
+    std::shared_ptr<GeomAPI_XY> aFlyoutPoint = aFlyOutAttr->pnt()->xy();
+    std::shared_ptr<GeomAPI_XY> anInterXY = anInter->xy();
+    std::shared_ptr<GeomAPI_XY> aDirIF = aFlyoutPoint->decreased(anInterXY);
+    std::shared_ptr<GeomAPI_XY> aDirIA = aPointA->xy()->decreased(anInterXY);
+    std::shared_ptr<GeomAPI_XY> aDirIB = aPointB->xy()->decreased(anInterXY);
+    double aSign = aDirIA->cross(aDirIB);
+    aSign /= fabs(aSign);
+    if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD)
+      aSign *= -1.0;
+
+    double cross1 = aSign * aDirIA->cross(aDirIF);
+    if (cross1 < -tolerance)
+      boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(!isReversed2);
+    double cross2 = aSign * aDirIF->cross(aDirIB);
+    if (cross2 < -tolerance)
+      boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(!isReversed1);
+
+    // the direction is reversed only once
+    if ((cross1 + tolerance) * (cross2 + tolerance) < 0.0) {
+      if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) {
+        convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_BACKWARD,
+                     (int)SketcherPrs_Tools::ANGLE_DIRECT);
+      }
+      convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT,
+                   (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY);
+      if (anAngleType == (int)SketcherPrs_Tools::ANGLE_BACKWARD) {
+        convertAngle(real(ANGLE_VALUE_ID()), (int)SketcherPrs_Tools::ANGLE_DIRECT,
+                     (int)SketcherPrs_Tools::ANGLE_BACKWARD);
+      }
+    }
 
-  aFlyOutAttr->setValue(aX, aY);
+    calculateAngle();
+  }
+  else {
+    // default position of the presentation
+    double aX = (aPointA->x() + aPointB->x() + anInter->x()) / 3.;
+    double aY = (aPointA->y() + aPointB->y() + anInter->y()) / 3.;
+    aFlyOutAttr->setValue(aX, aY);
+  }
   myFlyoutUpdate = false;
 
   return true;
 }
 
+void SketchPlugin_ConstraintAngle::updateVersion()
+{
+  bool aWasBlocked = data()->blockSendAttributeUpdated(true);
+
+  // Calculate angle value by the old algorithm and
+  // update the corresponding attribute to meet the new requirements.
+  FeaturePtr aLineA = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_A());
+  FeaturePtr aLineB = SketcherPrs_Tools::getFeatureLine(data(), ENTITY_B());
+
+  GeomPnt2dPtr aStartA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::START_ID());
+  GeomPnt2dPtr aEndA = SketcherPrs_Tools::getPoint(aLineA.get(), SketchPlugin_Line::END_ID());
+  GeomPnt2dPtr aStartB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::START_ID());
+  GeomPnt2dPtr aEndB = SketcherPrs_Tools::getPoint(aLineB.get(), SketchPlugin_Line::END_ID());
+
+  std::shared_ptr<GeomAPI_Angle2d> anAng;
+
+  if (boolean(ANGLE_REVERSED_FIRST_LINE_ID())->isInitialized() &&
+      boolean(ANGLE_REVERSED_SECOND_LINE_ID())->isInitialized()) {
+    bool isReversed1 = boolean(ANGLE_REVERSED_FIRST_LINE_ID())->value();
+    bool isReversed2 = boolean(ANGLE_REVERSED_SECOND_LINE_ID())->value();
+
+    std::shared_ptr<GeomAPI_Lin2d> aLine1(new GeomAPI_Lin2d(aStartA, aEndA));
+    std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB, aEndB));
+    anAng.reset(new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
+  }
+  else {
+    anAng.reset(new GeomAPI_Angle2d(aStartA, aEndA, aStartB, aEndB));
+
+    bool isReversed1 = anAng->isReversed(0);
+    bool isReversed2 = anAng->isReversed(1);
+
+    boolean(ANGLE_REVERSED_FIRST_LINE_ID())->setValue(isReversed1);
+    boolean(ANGLE_REVERSED_SECOND_LINE_ID())->setValue(isReversed2);
+  }
+  double anAngleValue = anAng->angleDegree();
+  double aConstValue = real(ANGLE_VALUE_ID())->value();
+
+  AttributeIntegerPtr aType = integer(TYPE_ID());
+  switch ((SketcherPrs_Tools::AngleType)aType->value()) {
+  case SketcherPrs_Tools::ANGLE_DIRECT:
+    if (anAngleValue < 0.0 && aConstValue > 180.0)
+      convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_BACKWARD,
+                                           SketcherPrs_Tools::ANGLE_DIRECT);
+    break;
+  case SketcherPrs_Tools::ANGLE_BACKWARD:
+    if (anAngleValue < 0.0 && aConstValue < 180.0)
+      convertAngle(real(ANGLE_VALUE_ID()), SketcherPrs_Tools::ANGLE_DIRECT,
+                                           SketcherPrs_Tools::ANGLE_BACKWARD);
+    break;
+  default:
+    break;
+  }
+  data()->blockSendAttributeUpdated(aWasBlocked, false);
+  integer(VERSION_ID())->setValue(THE_VERSION_1);
+}
+
 
 // ===============   Auxiliary functions   ==================================
 std::shared_ptr<GeomAPI_Pnt2d> intersect(FeaturePtr theLine1, FeaturePtr theLine2)
index 3983acff6b2f15c0ac0c205c8cc006205f9f2416..9305e1c20b79c791b863dba631dc975439fc0e48 100644 (file)
 #define SketchPlugin_ConstraintAngle_H_
 
 #include "SketchPlugin.h"
-#include <SketchPlugin_Sketch.h>
+#include "SketchPlugin_Sketch.h"
 #include "SketchPlugin_ConstraintBase.h"
 
+#include <ModelAPI_IReentrant.h>
+
 /** \class SketchPlugin_ConstraintAngle
  *  \ingroup Plugins
  *  \brief Feature for creation of a new constraint fix angle between two lines
  *  This constraint has two attributes:
  *  SketchPlugin_Constraint::ENTITY_A() and SketchPlugin_Constraint::ENTITY_B()
  */
-class SketchPlugin_ConstraintAngle : public SketchPlugin_ConstraintBase
+class SketchPlugin_ConstraintAngle : public SketchPlugin_ConstraintBase,
+                                     public ModelAPI_IReentrant
 {
- public:
+public:
   /// Angle constraint kind
   inline static const std::string& ID()
   {
@@ -53,6 +56,12 @@ class SketchPlugin_ConstraintAngle : public SketchPlugin_ConstraintBase
     static const std::string MY_TYPE_ID("AngleType");
     return MY_TYPE_ID;
   }
+  /// attribute name of previous value of operation type
+  inline static const std::string& PREV_TYPE_ID()
+  {
+    static const std::string MY_TYPE_ID("AngleTypePrevious");
+    return MY_TYPE_ID;
+  }
 
   /// attribute name of operation type
   inline static const std::string& ANGLE_VALUE_ID()
@@ -95,6 +104,18 @@ class SketchPlugin_ConstraintAngle : public SketchPlugin_ConstraintBase
     return MY_SELECTED_SECOND_POINT_ID;
   }
 
+public:
+  static const int THE_VERSION_0 = 0;
+  static const int THE_VERSION_1 = 20191210;
+
+  /// Attribute name of the version of Angle feature
+  inline static const std::string& VERSION_ID()
+  {
+    static const std::string MY_VERSION_ID("version");
+    return MY_VERSION_ID;
+  }
+
+public:
   /// \brief Creates a new part document if needed
   SKETCHPLUGIN_EXPORT virtual void execute();
 
@@ -117,6 +138,10 @@ class SketchPlugin_ConstraintAngle : public SketchPlugin_ConstraintBase
   /// Returns the AIS preview
   SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious);
 
+  /// Apply information of the message to current object.
+  /// It fills selected point and the first object.
+  virtual std::string processEvent(const std::shared_ptr<Events_Message>& theMessage);
+
   /// \brief Use plugin manager for features creation
   SketchPlugin_ConstraintAngle();
 
@@ -131,14 +156,16 @@ protected:
   /// The in/out angle is in degree.
   /// \param theAngle a source for the calculated angle
   /// \param a double angle value
-  double getAngleForType(double theAngle);
+  double getAngleForType(double theAngle, bool isReversed1 = false, bool isReversed2 = false);
 
   /// Update value of ANGLE_VALUE attribute according to the current type
   void updateAngleValue();
 
+  /// Update parameters of the Angle to meet requirements for the latest version
+  void updateVersion();
+
 private:
   bool myFlyoutUpdate; ///< to avoid cyclic dependencies on automatic updates of flyout point
-  int myPrevAngleType;
 };
 
 #endif
index ccab1cf931cf0b06a53dcdbdc2d9bdfd891b7cff..07d5638ad7e31790fa62d48a6ffe92499cc32029 100644 (file)
 
 #include "SketchPlugin_ConstraintCoincidenceInternal.h"
 
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Validator.h>
+
 SketchPlugin_ConstraintCoincidenceInternal::SketchPlugin_ConstraintCoincidenceInternal()
 {
 }
@@ -26,6 +30,12 @@ SketchPlugin_ConstraintCoincidenceInternal::SketchPlugin_ConstraintCoincidenceIn
 void SketchPlugin_ConstraintCoincidenceInternal::initAttributes()
 {
   SketchPlugin_ConstraintCoincidence::initAttributes();
+
+  data()->addAttribute(INDEX_ENTITY_A(), ModelAPI_AttributeInteger::typeId());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INDEX_ENTITY_A());
+
+  data()->addAttribute(INDEX_ENTITY_B(), ModelAPI_AttributeInteger::typeId());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INDEX_ENTITY_B());
 }
 
 void SketchPlugin_ConstraintCoincidenceInternal::execute()
index fac0060ebb86b2963ba8aeb8100bb30a6efafdcd..d5a65f8101163da9a1f5a2699144ba2801747d34 100644 (file)
@@ -30,7 +30,7 @@
 class SketchPlugin_ConstraintCoincidenceInternal : public SketchPlugin_ConstraintCoincidence
 {
   public:
-  /// Coincidence constraint kind
+  /// \brief Coincidence constraint kind
   inline static const std::string& ID()
   {
     static const std::string MY_CONSTRAINT_COINCIDENCE_ID("SketchConstraintCoincidenceInternal");
@@ -43,7 +43,20 @@ class SketchPlugin_ConstraintCoincidenceInternal : public SketchPlugin_Constrain
     return MY_KIND;
   }
 
-  /// Returns the AIS preview
+  /// \brief Index of point in the array if the first attribute is an array
+  inline static const std::string& INDEX_ENTITY_A()
+  {
+    static const std::string MY_INDEX("ConstraintEntityA_Index");
+    return MY_INDEX;
+  }
+  /// \brief Index of point in the array if the second attribute is an array
+  inline static const std::string& INDEX_ENTITY_B()
+  {
+    static const std::string MY_INDEX("ConstraintEntityB_Index");
+    return MY_INDEX;
+  }
+
+  /// \brief Returns the AIS preview
   SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious);
 
   /// \brief Creates a new part document if needed
diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp b/src/SketchPlugin/SketchPlugin_MacroBSpline.cpp
new file mode 100644 (file)
index 0000000..100e011
--- /dev/null
@@ -0,0 +1,400 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <SketchPlugin_MacroBSpline.h>
+
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
+#include <SketchPlugin_ConstraintCoincidenceInternal.h>
+#include <SketchPlugin_Line.h>
+#include <SketchPlugin_Point.h>
+#include <SketchPlugin_Tools.h>
+#include <SketchPlugin_Sketch.h>
+
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_AttributeRefAttrList.h>
+#include <ModelAPI_Events.h>
+#include <ModelAPI_EventReentrantMessage.h>
+#include <ModelAPI_Session.h>
+#include <ModelAPI_Validator.h>
+
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <GeomAlgoAPI_CompoundBuilder.h>
+#include <GeomAlgoAPI_EdgeBuilder.h>
+#include <GeomAlgoAPI_PointBuilder.h>
+
+#include <GeomAPI_BSpline2d.h>
+
+#include <sstream>
+
+/// Create internal coincidence constraint with B-spline pole
+static void createInternalConstraint(SketchPlugin_Sketch* theSketch,
+                                     AttributePtr thePoint,
+                                     AttributePtr theBSplinePoles,
+                                     const int thePoleIndex);
+
+
+SketchPlugin_MacroBSpline::SketchPlugin_MacroBSpline()
+  : SketchPlugin_SketchEntity(),
+    myDegree(3),
+    myIsPeriodic(false)
+{
+}
+
+SketchPlugin_MacroBSpline::SketchPlugin_MacroBSpline(bool isPeriodic)
+  : SketchPlugin_SketchEntity(),
+    myDegree(3),
+    myIsPeriodic(isPeriodic)
+{
+}
+
+void SketchPlugin_MacroBSpline::initAttributes()
+{
+  data()->addAttribute(POLES_ID(), GeomDataAPI_Point2DArray::typeId());
+  data()->addAttribute(WEIGHTS_ID(), ModelAPI_AttributeDoubleArray::typeId());
+
+  data()->addAttribute(REF_POLES_ID(), ModelAPI_AttributeRefAttrList::typeId());
+  ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), REF_POLES_ID());
+
+  data()->addAttribute(CONTROL_POLYGON_ID(), ModelAPI_AttributeBoolean::typeId());
+
+  data()->addAttribute(AUXILIARY_ID(), ModelAPI_AttributeBoolean::typeId());
+}
+
+void SketchPlugin_MacroBSpline::execute()
+{
+  FeaturePtr aBSpline = createBSplineFeature();
+
+  if (boolean(CONTROL_POLYGON_ID())->value()) {
+    std::list<FeaturePtr> aControlPoles;
+    createControlPolygon(aBSpline, aControlPoles);
+    constraintsForPoles(aControlPoles);
+
+    // message to init reentrant operation
+    static Events_ID anId = ModelAPI_EventReentrantMessage::eventId();
+    ReentrantMessagePtr aMessage(new ModelAPI_EventReentrantMessage(anId, this));
+    // set here the last pole to make coincidence with the start point of the next B-spline curve
+    aMessage->setCreatedFeature(aControlPoles.back());
+    Events_Loop::loop()->send(aMessage);
+  }
+}
+
+// LCOV_EXCL_START
+std::string SketchPlugin_MacroBSpline::processEvent(
+                                              const std::shared_ptr<Events_Message>& theMessage)
+{
+  ReentrantMessagePtr aReentrantMessage =
+      std::dynamic_pointer_cast<ModelAPI_EventReentrantMessage>(theMessage);
+  if (aReentrantMessage) {
+    FeaturePtr aCreatedFeature = aReentrantMessage->createdFeature();
+    ObjectPtr anObject = aReentrantMessage->selectedObject();
+    AttributePtr anAttribute = aReentrantMessage->selectedAttribute();
+    std::shared_ptr<GeomAPI_Pnt2d> aClickedPoint = aReentrantMessage->clickedPoint();
+
+    if (aClickedPoint) {
+      // fill points list (it consists of 2 points to make editable the second one)
+      AttributePoint2DArrayPtr aPointArrayAttr =
+          std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
+      aPointArrayAttr->setSize(2);
+      aPointArrayAttr->setPnt(0, aClickedPoint);
+      aPointArrayAttr->setPnt(1, aClickedPoint);
+
+      // fill weights
+      AttributeDoubleArrayPtr aWeightsArrayAttr = data()->realArray(WEIGHTS_ID());
+      aWeightsArrayAttr->setSize(2);
+      aWeightsArrayAttr->setValue(0, 1.0);
+      aWeightsArrayAttr->setValue(1, 1.0);
+
+      // fill reference attribute
+      AttributeRefAttrListPtr aRefAttrList =
+          std::dynamic_pointer_cast<ModelAPI_AttributeRefAttrList>(attribute(REF_POLES_ID()));
+      if (anAttribute) {
+        if (!anAttribute->owner() || !anAttribute->owner()->data()->isValid()) {
+          if (aCreatedFeature && anAttribute->id() == SketchPlugin_Point::COORD_ID())
+            anAttribute = aCreatedFeature->attribute(SketchPlugin_Point::COORD_ID());
+        }
+        aRefAttrList->append(anAttribute);
+      }
+    }
+    Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
+  }
+  return std::string();
+}
+// LCOV_EXCL_STOP
+
+FeaturePtr SketchPlugin_MacroBSpline::createBSplineFeature()
+{
+  if (myKnots.empty() || myMultiplicities.empty())
+    getAISObject(AISObjectPtr()); // fill B-spline parameters
+
+  FeaturePtr aBSpline = sketch()->addFeature(
+      myIsPeriodic ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID());
+
+  aBSpline->integer(SketchPlugin_BSplineBase::DEGREE_ID())->setValue(myDegree);
+
+  AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+      aBSpline->attribute(SketchPlugin_BSplineBase::POLES_ID()));
+  AttributePoint2DArrayPtr aPolesMacro =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
+  aPoles->assign(aPolesMacro);
+
+  AttributeDoubleArrayPtr aWeights =
+      aBSpline->data()->realArray(SketchPlugin_BSplineBase::WEIGHTS_ID());
+  AttributeDoubleArrayPtr aWeightsMacro = data()->realArray(WEIGHTS_ID());
+  int aSize = aWeightsMacro->size();
+  aWeights->setSize(aSize);
+  for (int index = 0; index < aSize; ++index)
+    aWeights->setValue(index, aWeightsMacro->value(index));
+
+  AttributeDoubleArrayPtr aKnots =
+      aBSpline->data()->realArray(SketchPlugin_BSplineBase::KNOTS_ID());
+  aSize = (int)myKnots.size();
+  aKnots->setSize(aSize);
+  std::list<double>::iterator aKIt = myKnots.begin();
+  for (int index = 0; index < aSize; ++index, ++aKIt)
+    aKnots->setValue(index, *aKIt);
+
+  AttributeIntArrayPtr aMults = aBSpline->data()->intArray(SketchPlugin_BSplineBase::MULTS_ID());
+  aSize = (int)myMultiplicities.size();
+  aMults->setSize(aSize);
+  std::list<int>::iterator aMIt = myMultiplicities.begin();
+  for (int index = 0; index < aSize; ++index, ++aMIt)
+    aMults->setValue(index, *aMIt);
+
+  if (!myIsPeriodic) {
+    AttributePoint2DPtr aStartPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+        aBSpline->attribute(SketchPlugin_BSpline::START_ID()));
+    aStartPoint->setValue(aPoles->pnt(0));
+
+    AttributePoint2DPtr aEndPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+       aBSpline->attribute(SketchPlugin_BSpline::END_ID()));
+    aEndPoint->setValue(aPoles->pnt(aPoles->size() - 1));
+  }
+
+  aBSpline->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->setValue(
+      boolean(AUXILIARY_ID())->value());
+
+  aBSpline->execute();
+
+  return aBSpline;
+}
+
+void SketchPlugin_MacroBSpline::createControlPolygon(FeaturePtr theBSpline,
+                                                     std::list<FeaturePtr>& thePoles)
+{
+  AttributePoint2DArrayPtr aPoles = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+      theBSpline->attribute(SketchPlugin_BSpline::POLES_ID()));
+  int aSize = aPoles->size();
+  // poles
+  for (int index = 0; index < aSize; ++index)
+    thePoles.push_back(createAuxiliaryPole(aPoles, index));
+  // segments
+  for (int index = 1; index < aSize; ++index)
+    createAuxiliarySegment(aPoles, index - 1, index);
+  if (myIsPeriodic) {
+    // additional segment to close the control polygon
+    createAuxiliarySegment(aPoles, aSize - 1, 0);
+  }
+}
+
+void SketchPlugin_MacroBSpline::constraintsForPoles(const std::list<FeaturePtr>& thePoles)
+{
+  AttributeRefAttrListPtr aRefAttrList = data()->refattrlist(REF_POLES_ID());
+  std::list<std::pair<ObjectPtr, AttributePtr> > aList;
+  if (aRefAttrList)
+    aList = aRefAttrList->list();
+
+  SketchPlugin_Sketch* aSketch = sketch();
+
+  std::list<std::pair<ObjectPtr, AttributePtr> >::iterator aLIt = aList.begin();
+  std::list<FeaturePtr>::const_iterator aPIt = thePoles.begin();
+  for (; aLIt != aList.end() && aPIt != thePoles.end(); ++aPIt, ++aLIt) {
+    // firstly, check the attribute (in this case the object will be not empty too)
+    if (aLIt->second) {
+      SketchPlugin_Tools::createConstraintAttrAttr(aSketch,
+          SketchPlugin_ConstraintCoincidence::ID(),
+          (*aPIt)->attribute(SketchPlugin_Point::COORD_ID()), aLIt->second);
+    }
+    // now add coincidence with the result
+    else if (aLIt->first) {
+      SketchPlugin_Tools::createConstraintAttrObject(aSketch,
+          SketchPlugin_ConstraintCoincidence::ID(),
+          (*aPIt)->attribute(SketchPlugin_Point::COORD_ID()), aLIt->first);
+    }
+  }
+}
+
+AISObjectPtr SketchPlugin_MacroBSpline::getAISObject(AISObjectPtr thePrevious)
+{
+  SketchPlugin_Sketch* aSketch = sketch();
+  if (!aSketch)
+    return AISObjectPtr();
+
+  AttributePoint2DArrayPtr aPolesArray =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(attribute(POLES_ID()));
+  AttributeDoubleArrayPtr aWeightsArray = data()->realArray(WEIGHTS_ID());
+
+  if (aPolesArray->size() < 2)
+    return AISObjectPtr();
+
+  std::list<GeomShapePtr> aShapes;
+
+  // convert poles to vertices and collect weights
+  std::list<GeomPnt2dPtr> aPoles2D;
+  std::list<double> aWeights;
+  for (int anIndex = 0; anIndex < aPolesArray->size(); ++anIndex) {
+    double aWeight = aWeightsArray->value(anIndex);
+    if (aWeight < 1.e-10)
+      continue; // skip poles with zero weights
+
+    aWeights.push_back(aWeight);
+
+    GeomPnt2dPtr aPole = aPolesArray->pnt(anIndex);
+    aPoles2D.push_back(aPole);
+    GeomPointPtr aPole3D = aSketch->to3D(aPole->x(), aPole->y());
+    aShapes.push_back(GeomAlgoAPI_PointBuilder::vertex(aPole3D));
+  }
+
+  // create result non-periodic B-spline curve
+  std::shared_ptr<GeomAPI_BSpline2d> aBSplineCurve;
+  try {
+    aBSplineCurve.reset(new GeomAPI_BSpline2d(aPoles2D, aWeights, myIsPeriodic));
+  } catch (...) {
+    // cannot build a B-spline curve
+    return AISObjectPtr();
+  }
+  GeomShapePtr anEdge =
+      GeomAlgoAPI_EdgeBuilder::bsplineOnPlane(aSketch->coordinatePlane(), aBSplineCurve);
+  if (!anEdge)
+    return AISObjectPtr();
+
+  // store transient parameters of B-spline curve
+  myDegree = aBSplineCurve->degree();
+  myKnots = aBSplineCurve->knots();
+  myMultiplicities = aBSplineCurve->mults();
+
+  aShapes.push_back(anEdge);
+  GeomShapePtr aCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
+
+  AISObjectPtr anAIS = thePrevious;
+  if (!anAIS)
+    anAIS.reset(new GeomAPI_AISObject());
+  anAIS->createShape(aCompound);
+
+  // Modify attributes
+  SketchPlugin_Tools::customizeFeaturePrs(anAIS, boolean(AUXILIARY_ID())->value());
+
+  return anAIS;
+}
+
+
+
+// ==========================     Auxiliary functions    ===========================================
+
+void SketchPlugin_MacroBSpline::assignDefaultNameForAux(FeaturePtr theAuxFeature,
+                                                        AttributePoint2DArrayPtr theBSplinePoles,
+                                                        const int thePoleIndex1,
+                                                        const int thePoleIndex2)
+{
+  FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner());
+
+  std::ostringstream aName;
+  aName << aBSpline->name();
+  if (theAuxFeature->getKind() == SketchPlugin_Point::ID())
+    aName << "_" << theBSplinePoles->id() << "_" << thePoleIndex1;
+  else
+    aName << "_segment_" << thePoleIndex1 << "_" << thePoleIndex2;
+
+  theAuxFeature->data()->setName(aName.str());
+  theAuxFeature->lastResult()->data()->setName(aName.str());
+}
+
+FeaturePtr SketchPlugin_MacroBSpline::createAuxiliaryPole(AttributePoint2DArrayPtr theBSplinePoles,
+                                                          const int thePoleIndex)
+{
+  FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner());
+
+  SketchPlugin_Sketch* aSketch =
+      std::dynamic_pointer_cast<SketchPlugin_Feature>(aBSpline)->sketch();
+
+  // create child point equal to the B-spline's pole
+  FeaturePtr aPointFeature = aSketch->addFeature(SketchPlugin_Point::ID());
+  aPointFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true);
+  aPointFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(aBSpline);
+
+  GeomPnt2dPtr aPole = theBSplinePoles->pnt(thePoleIndex);
+
+  AttributePoint2DPtr aCoord = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      aPointFeature->attribute(SketchPlugin_Point::COORD_ID()));
+  aCoord->setValue(aPole);
+
+  aPointFeature->execute();
+  assignDefaultNameForAux(aPointFeature, theBSplinePoles, thePoleIndex);
+
+  // internal constraint to keep position of the point
+  createInternalConstraint(aSketch, aCoord, theBSplinePoles, thePoleIndex);
+
+  return aPointFeature;
+}
+
+void SketchPlugin_MacroBSpline::createAuxiliarySegment(AttributePoint2DArrayPtr theBSplinePoles,
+                                                       const int thePoleIndex1,
+                                                       const int thePoleIndex2)
+{
+  FeaturePtr aBSpline = ModelAPI_Feature::feature(theBSplinePoles->owner());
+
+  SketchPlugin_Sketch* aSketch =
+      std::dynamic_pointer_cast<SketchPlugin_Feature>(aBSpline)->sketch();
+
+  // create child segment between B-spline poles
+  FeaturePtr aLineFeature = aSketch->addFeature(SketchPlugin_Line::ID());
+  aLineFeature->boolean(SketchPlugin_Point::AUXILIARY_ID())->setValue(true);
+  aLineFeature->reference(SketchPlugin_Point::PARENT_ID())->setValue(aBSpline);
+
+  AttributePoint2DPtr aLineStart = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+    aLineFeature->attribute(SketchPlugin_Line::START_ID()));
+  aLineStart->setValue(theBSplinePoles->pnt(thePoleIndex1));
+
+  AttributePoint2DPtr aLineEnd = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+    aLineFeature->attribute(SketchPlugin_Line::END_ID()));
+  aLineEnd->setValue(theBSplinePoles->pnt(thePoleIndex2));
+
+  aLineFeature->execute();
+  assignDefaultNameForAux(aLineFeature, theBSplinePoles, thePoleIndex1, thePoleIndex2);
+
+  // internal constraints to keep the segment position
+  createInternalConstraint(aSketch, aLineStart, theBSplinePoles, thePoleIndex1);
+  createInternalConstraint(aSketch, aLineEnd, theBSplinePoles, thePoleIndex2);
+}
+
+void createInternalConstraint(SketchPlugin_Sketch* theSketch,
+                              AttributePtr thePoint,
+                              AttributePtr theBSplinePoles,
+                              const int thePoleIndex)
+{
+  std::shared_ptr<SketchPlugin_ConstraintCoincidenceInternal> aConstraint =
+      std::dynamic_pointer_cast<SketchPlugin_ConstraintCoincidenceInternal>(
+      theSketch->addFeature(SketchPlugin_ConstraintCoincidenceInternal::ID()));
+  aConstraint->refattr(SketchPlugin_Constraint::ENTITY_A())->setAttr(thePoint);
+  aConstraint->refattr(SketchPlugin_Constraint::ENTITY_B())->setAttr(theBSplinePoles);
+  aConstraint->integer(SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B())
+      ->setValue(thePoleIndex);
+}
diff --git a/src/SketchPlugin/SketchPlugin_MacroBSpline.h b/src/SketchPlugin/SketchPlugin_MacroBSpline.h
new file mode 100644 (file)
index 0000000..d333325
--- /dev/null
@@ -0,0 +1,165 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketchPlugin_MacroBSpline_H_
+#define SketchPlugin_MacroBSpline_H_
+
+#include <ModelAPI_IReentrant.h>
+
+#include "SketchPlugin.h"
+
+#include "SketchPlugin_SketchEntity.h"
+
+#include <GeomAPI_IPresentable.h>
+
+class GeomAPI_Circ2d;
+class GeomAPI_Pnt2d;
+
+class GeomDataAPI_Point2DArray;
+
+/**\class SketchPlugin_MacroBSpline
+ * \ingroup Plugins
+ * \brief Feature for creation of the new B-spline in Sketch.
+ */
+class SketchPlugin_MacroBSpline : public SketchPlugin_SketchEntity,
+                                  public GeomAPI_IPresentable,
+                                  public ModelAPI_IReentrant
+{
+public:
+  /// B-spline macro feature kind
+  inline static const std::string& ID()
+  {
+    static const std::string ID("SketchMacroBSpline");
+    return ID;
+  }
+
+
+  /// list of B-spline poles
+  inline static const std::string& POLES_ID()
+  {
+    static const std::string ID("poles");
+    return ID;
+  }
+
+  /// list of references of B-spline poles
+  inline static const std::string& REF_POLES_ID()
+  {
+    static const std::string ID("poles_ref");
+    return ID;
+  }
+
+  /// list of B-spline weights
+  inline static const std::string& WEIGHTS_ID()
+  {
+    static const std::string ID("weights");
+    return ID;
+  }
+
+  /// flag attribute whether control polygon is need to be created
+  inline static const std::string& CONTROL_POLYGON_ID()
+  {
+    static const std::string ID("need_control_poly");
+    return ID;
+  }
+
+  /// Returns the kind of a feature
+  SKETCHPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = SketchPlugin_MacroBSpline::ID();
+    return MY_KIND;
+  }
+
+  /// \brief Request for initialization of data model of the feature: adding all attributes.
+  SKETCHPLUGIN_EXPORT virtual void initAttributes();
+
+  /// Returns the AIS preview
+  virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious);
+
+  /// Creates a new part document if needed
+  SKETCHPLUGIN_EXPORT virtual void execute();
+
+  /// Reimplemented from ModelAPI_Feature::isMacro().
+  /// \returns true
+  SKETCHPLUGIN_EXPORT virtual bool isMacro() const {return true;};
+
+  SKETCHPLUGIN_EXPORT virtual bool isPreviewNeeded() const {return false;};
+
+  /// Apply information of the message to current object. It fills reference object,
+  /// tangent type and tangent point refence in case of tangent arc
+  virtual std::string processEvent(const std::shared_ptr<Events_Message>& theMessage);
+
+  /// Use plugin manager for features creation
+  SketchPlugin_MacroBSpline();
+
+protected:
+  SketchPlugin_MacroBSpline(bool isPeriodic);
+
+private:
+  FeaturePtr createBSplineFeature();
+
+  void createControlPolygon(FeaturePtr theBSpline, std::list<FeaturePtr>& thePoles);
+  void constraintsForPoles(const std::list<FeaturePtr>& thePoles);
+
+  /// Create Point feature coincident with the B-spline pole
+  static FeaturePtr createAuxiliaryPole(std::shared_ptr<GeomDataAPI_Point2DArray> theBSplinePoles,
+                                        const int thePoleIndex);
+  /// Create segment between consequtive B-spline poles
+  static void createAuxiliarySegment(std::shared_ptr<GeomDataAPI_Point2DArray> theBSplinePoles,
+                                     const int thePoleIndex1,
+                                     const int thePoleIndex2);
+  /// Set name of auxiliary feature representing the control polygon
+  static void assignDefaultNameForAux(FeaturePtr theAuxFeature,
+                                      std::shared_ptr<GeomDataAPI_Point2DArray> theBSplinePoles,
+                                      const int thePoleIndex1,
+                                      const int thePoleIndex2 = -1);
+  friend class SketchPlugin_BSplineBase;
+
+private:
+  std::list<double> myKnots;
+  std::list<int> myMultiplicities;
+  int myDegree;
+  bool myIsPeriodic;
+};
+
+
+/**\class SketchPlugin_MacroBSpline
+* \ingroup Plugins
+* \brief Feature for creation of the new B-spline in Sketch.
+*/
+class SketchPlugin_MacroBSplinePeriodic : public SketchPlugin_MacroBSpline
+{
+public:
+  /// B-spline macro feature kind
+  inline static const std::string& ID()
+  {
+    static const std::string ID("SketchMacroBSplinePeriodic");
+    return ID;
+  }
+
+  /// Returns the kind of a feature
+  SKETCHPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    return SketchPlugin_MacroBSpline::ID();
+  }
+
+  /// Use plugin manager for features creation
+  SketchPlugin_MacroBSplinePeriodic() : SketchPlugin_MacroBSpline(true) {}
+};
+
+#endif
index 2cf1cbc7f011bcf9ff4c7a6e314d46c57f395705..630b735cef0f956daff11f63a1b0b21db302aeed 100644 (file)
@@ -44,6 +44,7 @@
 #include <GeomAlgoAPI_EdgeBuilder.h>
 #include <GeomAlgoAPI_PointBuilder.h>
 
+static const double TOLERANCE = 1.e-7;
 
 SketchPlugin_MacroEllipse::SketchPlugin_MacroEllipse()
 : SketchPlugin_SketchEntity(),
@@ -169,6 +170,9 @@ void SketchPlugin_MacroEllipse::attributeChanged(const std::string& theID)
       anEllipsePoints[0]->setY(0.5 * (anEllipsePoints[0]->y() + anEllipsePoints[1]->y()));
     }
 
+    if (anEllipsePoints[0]->distance(anEllipsePoints[1]) < TOLERANCE)
+      return; // ellipse is not valid
+
     std::shared_ptr<GeomAPI_Ellipse2d> anEllipse;
     if (aNbInitialized == 2) {
       GeomDir2dPtr aXDir(new GeomAPI_Dir2d(anEllipsePoints[1]->x() - anEllipsePoints[0]->x(),
index bd8290c0c5db33c4d749befe5146c0bfb09049a9..86f469d0bf6fa67132ce6e1a4a15b5382d1de651 100644 (file)
@@ -42,6 +42,7 @@
 #include <GeomAlgoAPI_PointBuilder.h>
 
 
+const double TOLERANCE = 1.e-7;
 const double paramTolerance = 1.e-4;
 const double PI = 3.141592653589793238463;
 
@@ -132,6 +133,9 @@ void SketchPlugin_MacroEllipticArc::attributeChanged(const std::string& theID)
   myStartPnt  = anEllipsePoints[2];
   myEndPnt    = anEllipsePoints[3];
 
+  if (myCenter->distance(myMajorAxis) < TOLERANCE)
+    return; // ellipse is not valid
+
   std::shared_ptr<GeomAPI_Ellipse2d> anEllipse;
   if (aNbInitialized == 2) {
     GeomDir2dPtr aXDir(new GeomAPI_Dir2d(anEllipsePoints[1]->x() - anEllipsePoints[0]->x(),
@@ -360,7 +364,7 @@ GeomShapePtr SketchPlugin_MacroEllipticArc::getArcShape()
     return GeomShapePtr();
 
   SketchPlugin_Sketch* aSketch = sketch();
-  if (!aSketch)
+  if (!aSketch || myCenter->distance(myMajorAxis) < 1.e-7)
     return GeomShapePtr();
 
   GeomPointPtr aCenter(aSketch->to3D(myCenter->x(), myCenter->y()));
index ac1b442c82da8b06b32c948a929aaa7427cbc7c4..db8995277246a8b5f079cea9fa7ac79e91e4e247 100644 (file)
@@ -24,6 +24,8 @@
 #include <SketchPlugin_IntersectionPoint.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_Arc.h>
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
 #include <SketchPlugin_Projection.h>
 #include <SketchPlugin_ConstraintAngle.h>
 #include <SketchPlugin_ConstraintCoincidence.h>
@@ -45,6 +47,7 @@
 #include <SketchPlugin_ConstraintTangent.h>
 #include <SketchPlugin_ConstraintVertical.h>
 #include <SketchPlugin_MacroArc.h>
+#include <SketchPlugin_MacroBSpline.h>
 #include <SketchPlugin_MacroCircle.h>
 #include <SketchPlugin_MultiRotation.h>
 #include <SketchPlugin_MultiTranslation.h>
@@ -147,6 +150,8 @@ SketchPlugin_Plugin::SketchPlugin_Plugin()
                               new SketchPlugin_SketchFeatureValidator);
   aFactory->registerValidator("SketchPlugin_MultiRotationAngleValidator",
                               new SketchPlugin_MultiRotationAngleValidator);
+  aFactory->registerValidator("SketchPlugin_BSplineValidator",
+                              new SketchPlugin_BSplineValidator);
 
   // register this plugin
   ModelAPI_Session::get()->registerPlugin(this);
@@ -201,6 +206,10 @@ FeaturePtr SketchPlugin_Plugin::createFeature(std::string theFeatureID)
     return FeaturePtr(new SketchPlugin_Circle);
   } else if (theFeatureID == SketchPlugin_Arc::ID()) {
     return FeaturePtr(new SketchPlugin_Arc);
+  } else if (theFeatureID == SketchPlugin_BSpline::ID()) {
+    return FeaturePtr(new SketchPlugin_BSpline);
+  } else if (theFeatureID == SketchPlugin_BSplinePeriodic::ID()) {
+    return FeaturePtr(new SketchPlugin_BSplinePeriodic);
   } else if (theFeatureID == SketchPlugin_Projection::ID()) {
     return FeaturePtr(new SketchPlugin_Projection);
   } else if (theFeatureID == SketchPlugin_ConstraintCoincidence::ID()) {
@@ -251,6 +260,10 @@ FeaturePtr SketchPlugin_Plugin::createFeature(std::string theFeatureID)
     return FeaturePtr(new SketchPlugin_Trim);
   } else if (theFeatureID == SketchPlugin_MacroArc::ID()) {
     return FeaturePtr(new SketchPlugin_MacroArc);
+  } else if (theFeatureID == SketchPlugin_MacroBSpline::ID()) {
+    return FeaturePtr(new SketchPlugin_MacroBSpline);
+  } else if (theFeatureID == SketchPlugin_MacroBSplinePeriodic::ID()) {
+    return FeaturePtr(new SketchPlugin_MacroBSplinePeriodic);
   } else if (theFeatureID == SketchPlugin_MacroCircle::ID()) {
     return FeaturePtr(new SketchPlugin_MacroCircle);
   } else if (theFeatureID == SketchPlugin_Ellipse::ID()) {
@@ -306,6 +319,8 @@ std::shared_ptr<ModelAPI_FeatureStateMessage> SketchPlugin_Plugin
       aMsg->setState(SketchPlugin_Line::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Circle::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Arc::ID(), aHasSketchPlane);
+      aMsg->setState(SketchPlugin_BSpline::ID(), aHasSketchPlane);
+      aMsg->setState(SketchPlugin_BSplinePeriodic::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Ellipse::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_EllipticArc::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Projection::ID(), aHasSketchPlane);
@@ -331,6 +346,8 @@ std::shared_ptr<ModelAPI_FeatureStateMessage> SketchPlugin_Plugin
       aMsg->setState(SketchPlugin_Split::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_Trim::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MacroArc::ID(), aHasSketchPlane);
+      aMsg->setState(SketchPlugin_MacroBSpline::ID(), aHasSketchPlane);
+      aMsg->setState(SketchPlugin_MacroBSplinePeriodic::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MacroCircle::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MacroEllipse::ID(), aHasSketchPlane);
       aMsg->setState(SketchPlugin_MacroEllipticArc::ID(), aHasSketchPlane);
index e04031fee6db7cee731dd447ce225900018759b9..e069a3567a2806864d22a508db7ac7ba0e91a16f 100644 (file)
@@ -20,6 +20,8 @@
 #include <SketchPlugin_Projection.h>
 
 #include <SketchPlugin_Arc.h>
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_Ellipse.h>
 #include <SketchPlugin_EllipticArc.h>
@@ -31,6 +33,8 @@
 #include <ModelAPI_AttributeRefAttr.h>
 #include <ModelAPI_AttributeSelection.h>
 #include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_ResultConstruction.h>
 #include <ModelAPI_Session.h>
 #include <ModelAPI_Validator.h>
@@ -39,6 +43,7 @@
 
 #include <Events_Loop.h>
 
+#include <GeomAPI_BSpline.h>
 #include <GeomAPI_Circ.h>
 #include <GeomAPI_Edge.h>
 #include <GeomAPI_Ellipse.h>
@@ -49,6 +54,7 @@
 #include <GeomAlgoAPI_EdgeBuilder.h>
 #include <GeomAlgoAPI_Projection.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 
 #include <cmath>
 
@@ -145,6 +151,16 @@ static const std::set<std::string>& ARC_PROJECTION()
   return aProj;
 }
 
+static const std::set<std::string>& BSPLINE_PROJECTION()
+{
+  static std::set<std::string> aProj;
+  if (aProj.empty()) {
+    aProj.insert(SketchPlugin_BSpline::ID());
+    aProj.insert(SketchPlugin_BSplinePeriodic::ID());
+  }
+  return aProj;
+}
+
 
 static const std::set<std::string>& possibleProjectionTypes(GeomEdgePtr theEdge,
                                                             GeomVertexPtr theVertex)
@@ -160,6 +176,8 @@ static const std::set<std::string>& possibleProjectionTypes(GeomEdgePtr theEdge,
       else
         return ARC_PROJECTION();
     }
+    else
+      return BSPLINE_PROJECTION();
   }
   static const std::set<std::string> DUMMY;
   return DUMMY;
@@ -196,8 +214,6 @@ void SketchPlugin_Projection::computeProjection(const std::string& theID)
   // if the type of feature differs with already selected, remove it and create once again
   bool isRebuild = rebuildProjectedFeature(aProjection, aProjType);
 
-  std::shared_ptr<GeomAPI_Pln> aSketchPlane = sketch()->plane();
-
   ResultConstructionPtr aResult =
       std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(lastResult());
   if (!isRebuild && aResult && aResult->shape() && theID == EXTERNAL_FEATURE_ID()) {
@@ -208,158 +224,14 @@ void SketchPlugin_Projection::computeProjection(const std::string& theID)
 
   keepCurrentFeature();
 
-  if (aVertex) {
-    std::shared_ptr<GeomAPI_Pnt> aPrjPnt = aSketchPlane->project(aVertex->point());
-    std::shared_ptr<GeomAPI_Pnt2d> aPntInSketch = sketch()->to2D(aPrjPnt);
+  bool isProjected = false;
+  if (aVertex)
+    isProjected = projectPoint(aProjection, aVertex->point());
+  else
+    isProjected = projectEdge(aProjection, anEdge);
 
-    rebuildProjectedFeature(aProjection, POINT_PROJECTION(), SketchPlugin_Point::ID());
-
-    // update coordinates of projection
-    std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-        aProjection->attribute(SketchPlugin_Point::COORD_ID()))->setValue(aPntInSketch);
-  }
-  else if (anEdge->isLine()) {
-    std::shared_ptr<GeomAPI_Pnt> aFirst = aSketchPlane->project(anEdge->firstPoint());
-    std::shared_ptr<GeomAPI_Pnt> aLast = aSketchPlane->project(anEdge->lastPoint());
-
-    std::shared_ptr<GeomAPI_Pnt2d> aFirstInSketch = sketch()->to2D(aFirst);
-    std::shared_ptr<GeomAPI_Pnt2d> aLastInSketch = sketch()->to2D(aLast);
-    if (aFirstInSketch->distance(aLastInSketch) < tolerance)
-      return; // line is semi-orthogonal to the sketch plane
-
-    rebuildProjectedFeature(aProjection, LINE_PROJECTION(), SketchPlugin_Line::ID());
-
-    // update attributes of projection
-    std::shared_ptr<GeomDataAPI_Point2D> aStartPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-        aProjection->attribute(SketchPlugin_Line::START_ID()));
-    std::shared_ptr<GeomDataAPI_Point2D> aEndPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-        aProjection->attribute(SketchPlugin_Line::END_ID()));
-    aStartPnt->setValue(aFirstInSketch);
-    aEndPnt->setValue(aLastInSketch);
-  }
-  else if (anEdge->isCircle() || anEdge->isArc() || anEdge->isEllipse()) {
-    GeomAlgoAPI_Projection aProjAlgo(aSketchPlane);
-    GeomCurvePtr aProjectedCurve = aProjAlgo.project(anEdge);
-
-    if (aProjectedCurve->isCircle()) {
-      GeomAPI_Circ aCircle(aProjectedCurve);
-      GeomPointPtr aCenter = aSketchPlane->project(aCircle.center());
-      GeomPnt2dPtr aCenterInSketch = sketch()->to2D(aCenter);
-
-      if (aProjectedCurve->isTrimmed()) {
-        // ARC is a projection
-        rebuildProjectedFeature(aProjection, ARC_PROJECTION(), SketchPlugin_Arc::ID());
-
-        GeomPointPtr aFirst = aProjectedCurve->getPoint(aProjectedCurve->startParam());
-        GeomPointPtr aLast = aProjectedCurve->getPoint(aProjectedCurve->endParam());
-        GeomPnt2dPtr aFirstInSketch = sketch()->to2D(aSketchPlane->project(aFirst));
-        GeomPnt2dPtr aLastInSketch = sketch()->to2D(aSketchPlane->project(aLast));
-
-        double aNormalsDot = aCircle.normal()->dot(aSketchPlane->direction());
-        if (fabs(fabs(aNormalsDot) - 1.0) > tolerance)
-          return; // arc is not in the plane, parallel to the sketch plane
-
-        bool isInversed = aNormalsDot < 0.;
-
-        bool aWasBlocked = aProjection->data()->blockSendAttributeUpdated(true);
-
-        // update attributes of projection
-        std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_Arc::CENTER_ID()));
-        std::shared_ptr<GeomDataAPI_Point2D> aStartPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_Arc::START_ID()));
-        std::shared_ptr<GeomDataAPI_Point2D> aEndPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_Arc::END_ID()));
-        aStartPnt->setValue(aFirstInSketch);
-        aEndPnt->setValue(aLastInSketch);
-        aCenterPnt->setValue(aCenterInSketch);
-        aProjection->boolean(SketchPlugin_Arc::REVERSED_ID())->setValue(isInversed);
-
-        aProjection->data()->blockSendAttributeUpdated(aWasBlocked);
-      }
-      else {
-        // CIRCLE is a projection
-        rebuildProjectedFeature(aProjection, CIRCLE_ELLIPSE_PROJECTION(),
-                                SketchPlugin_Circle::ID());
-
-        // update attributes of projection
-        std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_Circle::CENTER_ID()));
-        aCenterPnt->setValue(aCenterInSketch);
-        aProjection->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircle.radius());
-      }
-    }
-    else if (aProjectedCurve->isEllipse()) {
-      GeomAPI_Ellipse anEllipse(aProjectedCurve);
-      GeomPointPtr aCenter = aSketchPlane->project(anEllipse.center());
-      GeomPnt2dPtr aCenterInSketch = sketch()->to2D(aCenter);
-      GeomPointPtr aFocus = aSketchPlane->project(anEllipse.firstFocus());
-      GeomPnt2dPtr aFocusInSketch = sketch()->to2D(aFocus);
-
-      if (aProjectedCurve->isTrimmed()) {
-        // ELLIPTIC ARC is a projection
-        rebuildProjectedFeature(aProjection, ARC_PROJECTION(), SketchPlugin_EllipticArc::ID());
-
-        GeomPointPtr aFirst = aProjectedCurve->getPoint(aProjectedCurve->startParam());
-        GeomPointPtr aLast = aProjectedCurve->getPoint(aProjectedCurve->endParam());
-        GeomPnt2dPtr aFirstInSketch = sketch()->to2D(aSketchPlane->project(aFirst));
-        GeomPnt2dPtr aLastInSketch = sketch()->to2D(aSketchPlane->project(aLast));
-
-        double aNormalsDot = anEllipse.normal()->dot(aSketchPlane->direction());
-        if (fabs(fabs(aNormalsDot) - 1.0) > tolerance)
-          return; // arc is not in the plane, parallel to the sketch plane
-
-        bool isInversed = aNormalsDot < 0.;
-
-        bool aWasBlocked = aProjection->data()->blockSendAttributeUpdated(true);
-
-        // update attributes of projection
-        std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_EllipticArc::CENTER_ID()));
-        std::shared_ptr<GeomDataAPI_Point2D> aFocusPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()));
-        std::shared_ptr<GeomDataAPI_Point2D> aStartPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_EllipticArc::START_POINT_ID()));
-        std::shared_ptr<GeomDataAPI_Point2D> aEndPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_EllipticArc::END_POINT_ID()));
-        aStartPnt->setValue(aFirstInSketch);
-        aEndPnt->setValue(aLastInSketch);
-        aCenterPnt->setValue(aCenterInSketch);
-        aFocusPnt->setValue(aFocusInSketch);
-        aProjection->boolean(SketchPlugin_EllipticArc::REVERSED_ID())->setValue(isInversed);
-
-        aProjection->data()->blockSendAttributeUpdated(aWasBlocked);
-      }
-      else {
-        // ELLIPSE is a projection
-        rebuildProjectedFeature(aProjection, CIRCLE_ELLIPSE_PROJECTION(),
-                                SketchPlugin_Ellipse::ID());
-
-        // update attributes of projection
-        std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_Ellipse::CENTER_ID()));
-        aCenterPnt->setValue(aCenterInSketch);
-        std::shared_ptr<GeomDataAPI_Point2D> aFocusPnt =
-            std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
-            aProjection->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()));
-        aFocusPnt->setValue(aFocusInSketch);
-        aProjection->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(
-            anEllipse.minorRadius());
-      }
-    }
-    else
-      return;
-  } else
-    return;
+  if (!isProjected)
+    return; // projection is not computed, stop processing
 
   aProjection->boolean(COPY_ID())->setValue(true);
   aProjection->execute();
@@ -407,3 +279,275 @@ bool SketchPlugin_Projection::rebuildProjectedFeature(
     theProjection = sketch()->addFeature(theRequestedFeature);
   return isRebuild;
 }
+
+bool SketchPlugin_Projection::projectPoint(FeaturePtr& theProjection, const GeomPointPtr& thePoint)
+{
+  std::shared_ptr<GeomAPI_Pln> aSketchPlane = sketch()->plane();
+
+  std::shared_ptr<GeomAPI_Pnt> aPrjPnt = aSketchPlane->project(thePoint);
+  std::shared_ptr<GeomAPI_Pnt2d> aPntInSketch = sketch()->to2D(aPrjPnt);
+
+  rebuildProjectedFeature(theProjection, POINT_PROJECTION(), SketchPlugin_Point::ID());
+
+  // update coordinates of projection
+  std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Point::COORD_ID()))->setValue(aPntInSketch);
+  return true;
+}
+
+bool SketchPlugin_Projection::projectSegment(FeaturePtr& theProjection, const GeomEdgePtr& theEdge)
+{
+  std::shared_ptr<GeomAPI_Pln> aSketchPlane = sketch()->plane();
+
+  std::shared_ptr<GeomAPI_Pnt> aFirst = aSketchPlane->project(theEdge->firstPoint());
+  std::shared_ptr<GeomAPI_Pnt> aLast = aSketchPlane->project(theEdge->lastPoint());
+
+  std::shared_ptr<GeomAPI_Pnt2d> aFirstInSketch = sketch()->to2D(aFirst);
+  std::shared_ptr<GeomAPI_Pnt2d> aLastInSketch = sketch()->to2D(aLast);
+  if (aFirstInSketch->distance(aLastInSketch) < tolerance)
+    return false; // line is semi-orthogonal to the sketch plane
+
+  rebuildProjectedFeature(theProjection, LINE_PROJECTION(), SketchPlugin_Line::ID());
+
+  // update attributes of projection
+  std::shared_ptr<GeomDataAPI_Point2D> aStartPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Line::START_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aEndPnt = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Line::END_ID()));
+  aStartPnt->setValue(aFirstInSketch);
+  aEndPnt->setValue(aLastInSketch);
+
+  return true;
+}
+
+bool SketchPlugin_Projection::projectEdge(FeaturePtr& theProjection, const GeomEdgePtr& theEdge)
+{
+  if (theEdge->isLine())
+    return projectSegment(theProjection, theEdge);
+
+  std::shared_ptr<GeomAPI_Pln> aSketchPlane = sketch()->plane();
+
+  GeomAlgoAPI_Projection aProjAlgo(aSketchPlane);
+  GeomCurvePtr aProjectedCurve = aProjAlgo.project(theEdge);
+
+  bool isOk = false;
+  if (aProjectedCurve->isCircle()) {
+    if (aProjectedCurve->isTrimmed()) {
+      // ARC is a projection
+      isOk = fillArc(theProjection, aProjectedCurve, aSketchPlane);
+    }
+    else {
+      // CIRCLE is a projection
+      isOk = fillCircle(theProjection, aProjectedCurve, aSketchPlane);
+    }
+  }
+  else if (aProjectedCurve->isEllipse()) {
+    if (aProjectedCurve->isTrimmed()) {
+      // ELLIPTIC ARC is a projection
+      isOk = fillEllipticArc(theProjection, aProjectedCurve, aSketchPlane);
+    }
+    else {
+      // ELLIPSE is a projection
+      isOk = fillEllipse(theProjection, aProjectedCurve, aSketchPlane);
+    }
+  }
+  else
+    isOk = fillBSpline(theProjection, aProjectedCurve, aSketchPlane);
+
+  return isOk;
+}
+
+bool SketchPlugin_Projection::fillArc(FeaturePtr& theProjection,
+                                      const GeomCurvePtr& theArc,
+                                      const GeomPlanePtr& thePlane)
+{
+  rebuildProjectedFeature(theProjection, ARC_PROJECTION(), SketchPlugin_Arc::ID());
+
+  GeomAPI_Circ aCircle(theArc);
+
+  double aNormalsDot = aCircle.normal()->dot(thePlane->direction());
+  if (fabs(fabs(aNormalsDot) - 1.0) > tolerance)
+    return false; // arc is not in the plane, parallel to the sketch plane
+
+  bool isInversed = aNormalsDot < 0.;
+
+  GeomPointPtr aCenter = thePlane->project(aCircle.center());
+  GeomPnt2dPtr aCenterInSketch = sketch()->to2D(aCenter);
+
+  GeomPointPtr aFirst = theArc->getPoint(theArc->startParam());
+  GeomPnt2dPtr aFirstInSketch = sketch()->to2D(thePlane->project(aFirst));
+
+  GeomPointPtr aLast = theArc->getPoint(theArc->endParam());
+  GeomPnt2dPtr aLastInSketch = sketch()->to2D(thePlane->project(aLast));
+
+  bool aWasBlocked = theProjection->data()->blockSendAttributeUpdated(true);
+
+  // update attributes of projection
+  std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Arc::CENTER_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aStartPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Arc::START_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aEndPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Arc::END_ID()));
+  aStartPnt->setValue(aFirstInSketch);
+  aEndPnt->setValue(aLastInSketch);
+  aCenterPnt->setValue(aCenterInSketch);
+  theProjection->boolean(SketchPlugin_Arc::REVERSED_ID())->setValue(isInversed);
+
+  theProjection->data()->blockSendAttributeUpdated(aWasBlocked);
+  return true;
+}
+
+bool SketchPlugin_Projection::fillCircle(FeaturePtr& theProjection,
+                                         const GeomCurvePtr& theCircle,
+                                         const GeomPlanePtr& thePlane)
+{
+  rebuildProjectedFeature(theProjection, CIRCLE_ELLIPSE_PROJECTION(), SketchPlugin_Circle::ID());
+
+  GeomAPI_Circ aCircle(theCircle);
+  GeomPointPtr aCenter = thePlane->project(aCircle.center());
+  GeomPnt2dPtr aCenterInSketch = sketch()->to2D(aCenter);
+
+  // update attributes of projection
+  std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Circle::CENTER_ID()));
+  aCenterPnt->setValue(aCenterInSketch);
+  theProjection->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircle.radius());
+  return true;
+}
+
+bool SketchPlugin_Projection::fillEllipse(FeaturePtr& theProjection,
+                                          const GeomCurvePtr& theEllipse,
+                                          const GeomPlanePtr& thePlane)
+{
+  rebuildProjectedFeature(theProjection, CIRCLE_ELLIPSE_PROJECTION(), SketchPlugin_Ellipse::ID());
+
+  GeomAPI_Ellipse anEllipse(theEllipse);
+  GeomPointPtr aCenter = thePlane->project(anEllipse.center());
+  GeomPnt2dPtr aCenterInSketch = sketch()->to2D(aCenter);
+  GeomPointPtr aFocus = thePlane->project(anEllipse.firstFocus());
+  GeomPnt2dPtr aFocusInSketch = sketch()->to2D(aFocus);
+
+  // update attributes of projection
+  std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Ellipse::CENTER_ID()));
+  aCenterPnt->setValue(aCenterInSketch);
+  std::shared_ptr<GeomDataAPI_Point2D> aFocusPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()));
+  aFocusPnt->setValue(aFocusInSketch);
+  theProjection->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(anEllipse.minorRadius());
+  return true;
+}
+
+bool SketchPlugin_Projection::fillEllipticArc(FeaturePtr& theProjection,
+                                              const GeomCurvePtr& theEllipticArc,
+                                              const GeomPlanePtr& thePlane)
+{
+  rebuildProjectedFeature(theProjection, ARC_PROJECTION(), SketchPlugin_EllipticArc::ID());
+
+  GeomAPI_Ellipse anEllipse(theEllipticArc);
+
+  double aNormalsDot = anEllipse.normal()->dot(thePlane->direction());
+  if (fabs(fabs(aNormalsDot) - 1.0) > tolerance)
+    return false; // arc is not in the plane, parallel to the sketch plane
+
+  bool isInversed = aNormalsDot < 0.;
+
+  GeomPointPtr aCenter = thePlane->project(anEllipse.center());
+  GeomPnt2dPtr aCenterInSketch = sketch()->to2D(aCenter);
+  GeomPointPtr aFocus = thePlane->project(anEllipse.firstFocus());
+  GeomPnt2dPtr aFocusInSketch = sketch()->to2D(aFocus);
+
+  GeomPointPtr aFirst = theEllipticArc->getPoint(theEllipticArc->startParam());
+  GeomPnt2dPtr aFirstInSketch = sketch()->to2D(thePlane->project(aFirst));
+  GeomPointPtr aLast = theEllipticArc->getPoint(theEllipticArc->endParam());
+  GeomPnt2dPtr aLastInSketch = sketch()->to2D(thePlane->project(aLast));
+
+  bool aWasBlocked = theProjection->data()->blockSendAttributeUpdated(true);
+
+  // update attributes of projection
+  std::shared_ptr<GeomDataAPI_Point2D> aCenterPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_EllipticArc::CENTER_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aFocusPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aStartPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_EllipticArc::START_POINT_ID()));
+  std::shared_ptr<GeomDataAPI_Point2D> aEndPnt =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+      theProjection->attribute(SketchPlugin_EllipticArc::END_POINT_ID()));
+  aStartPnt->setValue(aFirstInSketch);
+  aEndPnt->setValue(aLastInSketch);
+  aCenterPnt->setValue(aCenterInSketch);
+  aFocusPnt->setValue(aFocusInSketch);
+  theProjection->boolean(SketchPlugin_EllipticArc::REVERSED_ID())->setValue(isInversed);
+
+  theProjection->data()->blockSendAttributeUpdated(aWasBlocked);
+  return true;
+}
+
+bool SketchPlugin_Projection::fillBSpline(FeaturePtr& theProjection,
+                                          const GeomCurvePtr& theCurve,
+                                          const GeomPlanePtr& thePlane)
+{
+  GeomAPI_BSpline aBSpline(theCurve);
+
+  rebuildProjectedFeature(theProjection, BSPLINE_PROJECTION(),
+      aBSpline.isPeriodic() ? SketchPlugin_BSplinePeriodic::ID() : SketchPlugin_BSpline::ID());
+
+  theProjection->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline.degree());
+
+  AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(
+      theProjection->attribute(SketchPlugin_BSpline::POLES_ID()));
+  std::list<GeomPointPtr> aPoles = aBSpline.poles();
+  aPolesAttr->setSize((int)aPoles.size());
+  std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
+  for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
+    GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
+    aPolesAttr->setPnt(anIndex, aPoleInSketch);
+  }
+
+  AttributeDoubleArrayPtr aWeightsAttr =
+      theProjection->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
+  std::list<double> aWeights = aBSpline.weights();
+  if (aWeights.empty()) { // rational B-spline
+    int aSize = (int)aPoles.size();
+    aWeightsAttr->setSize(aSize);
+    for (int anIndex = 0; anIndex < aSize; ++anIndex)
+      aWeightsAttr->setValue(anIndex, 1.0);
+  }
+  else { // non-rational B-spline
+    aWeightsAttr->setSize((int)aWeights.size());
+    std::list<double>::iterator anIt = aWeights.begin();
+    for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
+      aWeightsAttr->setValue(anIndex, *anIt);
+  }
+
+  AttributeDoubleArrayPtr aKnotsAttr =
+      theProjection->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
+  std::list<double> aKnots = aBSpline.knots();
+  int aSize = (int)aKnots.size();
+  aKnotsAttr->setSize(aSize);
+  std::list<double>::iterator aKIt = aKnots.begin();
+  for (int index = 0; index < aSize; ++index, ++aKIt)
+    aKnotsAttr->setValue(index, *aKIt);
+
+  AttributeIntArrayPtr aMultsAttr =
+      theProjection->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
+  std::list<int> aMultiplicities = aBSpline.mults();
+  aSize = (int)aMultiplicities.size();
+  aMultsAttr->setSize(aSize);
+  std::list<int>::iterator aMIt = aMultiplicities.begin();
+  for (int index = 0; index < aSize; ++index, ++aMIt)
+    aMultsAttr->setValue(index, *aMIt);
+
+  return true;
+}
index 40fd6301a6933faafea9675ea4c5b3d8a6a58e5c..b820daf991e95a19bfd0dfe6fa00e84580aaed3b 100644 (file)
@@ -22,6 +22,8 @@
 
 #include "SketchPlugin_SketchEntity.h"
 
+class GeomAPI_Curve;
+
 /** \class SketchPlugin_Projection
  *  \ingroup Plugins
  *  \brief Feature for creation of external feature as a projection onto the sketch plane.
@@ -88,6 +90,34 @@ private:
   /// \brief Find projection of a feature onto sketch plane
   void computeProjection(const std::string& theID);
 
+  /// \brief Project point to the sketch plane
+  bool projectPoint(FeaturePtr& theProjection, const std::shared_ptr<GeomAPI_Pnt>& thePoint);
+  /// \brief Project segment to the sketch plane
+  bool projectSegment(FeaturePtr& theProjection, const std::shared_ptr<GeomAPI_Edge>& theEdge);
+  /// \brief Project any edge to sketch plane
+  bool projectEdge(FeaturePtr& theProjection, const std::shared_ptr<GeomAPI_Edge>& theEdge);
+
+  /// \brief Fill attributes of the Arc feature
+  bool fillArc(FeaturePtr& theProjection,
+               const std::shared_ptr<GeomAPI_Curve>& theArc,
+               const std::shared_ptr<GeomAPI_Pln>& thePlane);
+  /// \brief Fill attributes of the Circle feature
+  bool fillCircle(FeaturePtr& theProjection,
+                  const std::shared_ptr<GeomAPI_Curve>& theCircle,
+                  const std::shared_ptr<GeomAPI_Pln>& thePlane);
+  /// \brief Fill attributes of the Ellipse feature
+  bool fillEllipse(FeaturePtr& theProjection,
+                   const std::shared_ptr<GeomAPI_Curve>& theEllipse,
+                   const std::shared_ptr<GeomAPI_Pln>& thePlane);
+  /// \brief Fill attributes of the EllipticArc feature
+  bool fillEllipticArc(FeaturePtr& theProjection,
+                       const std::shared_ptr<GeomAPI_Curve>& theEllipticArc,
+                       const std::shared_ptr<GeomAPI_Pln>& thePlane);
+  /// \brief Fill attributes of the B-spline feature
+  bool fillBSpline(FeaturePtr& theProjection,
+                   const std::shared_ptr<GeomAPI_Curve>& theCurve,
+                   const std::shared_ptr<GeomAPI_Pln>& thePlane);
+
   /// \brief Delete already calculated projected feature
   ///        if the selection of the projection is changed
   /// \param[in/out] theProjection   projected feature
index b0a5658ffef5145a4ee4c13906f34caa50aed772..62dad8c5fd74ef09b9ba09aaec9dd06b41c06229 100644 (file)
@@ -1164,8 +1164,16 @@ FeaturePtr SketchPlugin_Split::splitClosed(FeaturePtr& theSplitFeature,
         aRefsToParent.push_back(*aRef);
     }
     for (std::list<AttributePtr>::iterator aRef = aRefsToParent.begin();
-         aRef != aRefsToParent.end(); ++aRef)
-      std::dynamic_pointer_cast<ModelAPI_AttributeReference>(*aRef)->setValue(theSplitFeature);
+         aRef != aRefsToParent.end(); ++aRef) {
+      std::dynamic_pointer_cast<ModelAPI_AttributeReference>(*aRef)->setValue(
+          theBaseFeatureModified);
+
+      FeaturePtr anOwner = ModelAPI_Feature::feature((*aRef)->owner());
+      SketchPlugin_Tools::replaceInName(anOwner,
+          aBaseFeature->name(), theBaseFeatureModified->name());
+      SketchPlugin_Tools::replaceInName(anOwner->lastResult(),
+          aBaseFeature->name(), theBaseFeatureModified->name());
+    }
   }
 
   theCreatedFeatures.insert(theBaseFeatureModified);
index 0147ba521d38ed1f2be00ff5aa870a3d85f3f6a3..df8e35cf60ccad6dddc455524dc1dda558dbfaac 100644 (file)
@@ -563,6 +563,17 @@ void setDimensionColor(const AISObjectPtr& theDimPrs)
     theDimPrs->setColor(aColor[0], aColor[1], aColor[2]);
 }
 
+void replaceInName(ObjectPtr theObject, const std::string& theSource, const std::string& theDest)
+{
+  std::string aName = theObject->data()->name();
+  size_t aPos = aName.find(theSource);
+  if (aPos != std::string::npos) {
+    std::string aNewName = aName.substr(0, aPos) + theDest
+                         + aName.substr(aPos + theSource.size());
+    theObject->data()->setName(aNewName);
+  }
+}
+
 } // namespace SketchPlugin_Tools
 
 
index a45ecf05a85899eeef033f8964028dabdc8e3e9c..1a12945c955a508a0c5a81a82266aa2bea2b9725 100644 (file)
@@ -146,6 +146,9 @@ void customizeFeaturePrs(const AISObjectPtr& thePrs, bool isAxiliary);
 
 void setDimensionColor(const AISObjectPtr& theDimPrs);
 
+/// Replace string in the name of object
+void replaceInName(ObjectPtr theObject, const std::string& theSource, const std::string& theDest);
+
 }; // namespace SketchPlugin_Tools
 
 namespace SketchPlugin_SegmentationTools
index 2957e031573500609257d190e2295300248c2712..1b56a1884172e56678eabd384099c077a0090e54 100644 (file)
@@ -1065,8 +1065,14 @@ FeaturePtr SketchPlugin_Trim::trimClosed(const std::shared_ptr<GeomAPI_Pnt2d>& t
         aRefsToParent.push_back(*aRef);
     }
     for (std::list<AttributePtr>::iterator aRef = aRefsToParent.begin();
-         aRef != aRefsToParent.end(); ++aRef)
+         aRef != aRefsToParent.end(); ++aRef) {
       std::dynamic_pointer_cast<ModelAPI_AttributeReference>(*aRef)->setValue(anNewFeature);
+
+      FeaturePtr anOwner = ModelAPI_Feature::feature((*aRef)->owner());
+      SketchPlugin_Tools::replaceInName(anOwner, aBaseFeature->name(), anNewFeature->name());
+      SketchPlugin_Tools::replaceInName(anOwner->lastResult(),
+          aBaseFeature->name(), anNewFeature->name());
+    }
   }
 
   const std::string& aStartAttrName = anNewFeature->getKind() == SketchPlugin_Arc::ID() ?
index 454a476ee2a6498ceaad57ed083b46071744b8c5..94236695e053cfa67cba433d1e51ca1f970354ae 100644 (file)
@@ -65,7 +65,9 @@
 #include <GeomAPI_Lin.h>
 #include <GeomAPI_Edge.h>
 #include <GeomAPI_Vertex.h>
+
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 
 #include <algorithm>
 #include <cmath>
@@ -946,8 +948,16 @@ bool SketchPlugin_SplitValidator::isValid(const AttributePtr& theAttribute,
   }
   AttributeReferencePtr aFeatureAttr =
             std::dynamic_pointer_cast<ModelAPI_AttributeReference>(theAttribute);
+  std::shared_ptr<SketchPlugin_Feature> aSplitFeature =
+    std::dynamic_pointer_cast<SketchPlugin_Feature>(theAttribute->owner());
 
   ObjectPtr anAttrObject = aFeatureAttr->value();
+  if (!anAttrObject) {
+    AttributePtr aPreviewAttr = aSplitFeature->attribute(SketchPlugin_Trim::PREVIEW_OBJECT());
+    aFeatureAttr = std::dynamic_pointer_cast<ModelAPI_AttributeReference>(aPreviewAttr);
+    anAttrObject = aFeatureAttr->value();
+  }
+
   FeaturePtr anAttrFeature = ModelAPI_Feature::feature(anAttrObject);
   if (!anAttrFeature)
     return aValid;
@@ -1115,38 +1125,35 @@ bool SketchPlugin_ProjectionValidator::isValid(const AttributePtr& theAttribute,
   std::shared_ptr<GeomAPI_Dir> aNormal = aPlane->direction();
   std::shared_ptr<GeomAPI_Pnt> anOrigin = aPlane->location();
 
+  bool aValid = true;
   if (anEdge->isLine()) {
     std::shared_ptr<GeomAPI_Lin> aLine = anEdge->line();
     std::shared_ptr<GeomAPI_Dir> aLineDir = aLine->direction();
     double aDot = fabs(aNormal->dot(aLineDir));
-    bool aValid = fabs(aDot - 1.0) >= tolerance * tolerance;
+    aValid = fabs(aDot - 1.0) >= tolerance * tolerance;
     if (!aValid)
       theError = "Error: Line is orthogonal to the sketch plane.";
-    return aValid;
   }
   else if (anEdge->isCircle() || anEdge->isArc()) {
     std::shared_ptr<GeomAPI_Circ> aCircle = anEdge->circle();
     std::shared_ptr<GeomAPI_Dir> aCircNormal = aCircle->normal();
     double aDot = fabs(aNormal->dot(aCircNormal));
-    bool aValid = aDot >= tolerance * tolerance;
+    aValid = aDot >= tolerance * tolerance;
     if (!aValid)
       theError.arg(anEdge->isCircle() ? "Error: Circle is orthogonal to the sketch plane."
                                       : "Error: Arc is orthogonal to the sketch plane.");
-    return aValid;
   }
   else if (anEdge->isEllipse()) {
     std::shared_ptr<GeomAPI_Ellipse> anEllipse = anEdge->ellipse();
     std::shared_ptr<GeomAPI_Dir> anEllipseNormal = anEllipse->normal();
     double aDot = fabs(aNormal->dot(anEllipseNormal));
-    bool aValid = fabs(aDot - 1.0) <= tolerance * tolerance;
+    aValid = fabs(aDot - 1.0) <= tolerance * tolerance;
     if (!aValid)
       theError.arg(anEdge->isClosed() ? "Error: Ellipse is orthogonal to the sketch plane."
                                       : "Error: Elliptic Arc is orthogonal to the sketch plane.");
-    return aValid;
   }
 
-  theError = "Error: Selected object is not supported for projection.";
-  return false;
+  return aValid;
 }
 
 
@@ -1771,3 +1778,20 @@ bool SketchPlugin_MultiRotationAngleValidator::isValid(const AttributePtr& theAt
 
   return true;
 }
+
+bool SketchPlugin_BSplineValidator::isValid(const AttributePtr& theAttribute,
+                                            const std::list<std::string>& theArguments,
+                                            Events_InfoMessage& theError) const
+{
+  AttributePoint2DArrayPtr aPolesArray =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(theAttribute);
+  if (!aPolesArray)
+    return false;
+
+  if (aPolesArray->size() < 2) {
+    theError = "Number of B-spline poles should be 2 or more";
+    return false;
+  }
+
+  return true;
+}
index b3fe8f57464ee41781dcfe9d114f9caaac735a89..1b20c45fdcdb45af3e4028af684cb9cd0c6e1c7f 100644 (file)
@@ -531,4 +531,19 @@ class SketchPlugin_MultiRotationAngleValidator : public ModelAPI_AttributeValida
                        Events_InfoMessage& theError) const;
 };
 
+/**\class SketchPlugin_BSplineValidator
+ * \ingroup Validators
+ * \brief Validator for checking poles/weights of B-spline curve.
+ */
+class SketchPlugin_BSplineValidator : public ModelAPI_AttributeValidator
+{
+  //! returns true if attribute is valid
+  //! \param theAttribute the checked attribute
+  //! \param theArguments arguments of the attribute
+  //! \param theError error message
+  virtual bool isValid(const AttributePtr& theAttribute,
+                       const std::list<std::string>& theArguments,
+                       Events_InfoMessage& theError) const;
+};
+
 #endif
index d22b51e8cacc9288bc29c1b71b25e0d1a259a56d..ae9a9a884df7b24d8aa6deed254f970bf64468a5 100644 (file)
       <source>Sketch</source>
       <translation>Esquisse</translation>
     </message>
+    <message>
+      <source>&lt;b&gt;The constraint is conflicting with others. To fix this, you can either &lt;font color='red'&gt;undo (Ctrl+Z)&lt;/font&gt; your operation or &lt;font color='red'&gt;remove&lt;/font&gt; a conflicting constraint.&lt;/b&gt;</source>
+      <translation>&lt;b&gt;La contrainte est en conflit avec d'autres. Pour y remédier, vous pouvez soit &lt;font color='red'&gt;annuler (Ctrl+Z)&lt;/font&gt; votre opération, soit &lt;font color='red'&gt;supprimer&lt;/font&gt; une contrainte conflictuelle.&lt;/b&gt;</translation>
+    </message>
   </context>
   <context>
     <name>Sketch:External</name>
       <source>The constraint is conflicting with others. To fix this, you can either undo your operation or remove a conflicting constraint.</source>
       <translation>La contrainte est en conflit avec les autres. Pour résoudre ce problème, vous pouvez annuler votre opération ou supprimer une contrainte en conflit.</translation>
     </message>
-  </context>
-  <context>
-    <name>Sketch:SketchPlugin_SolverErrorValidator</name>
     <message>
       <source>The set of constraints lead to degenerated geometry. To fix this, you can either undo your operation or remove a constraint or the degenerated geometry.</source>
       <translation>L&apos;ensemble des contraintes conduit à une géométrie dégénérée. Pour résoudre ce problème, vous pouvez annuler votre opération ou supprimer une contrainte ou la géométrie dégénérée..</translation>
     </message>
+    <message>
+      <source>&lt;b&gt;The constraint is conflicting with others. To fix this, you can either &lt;font color='red'&gt;undo (Ctrl+Z)&lt;/font&gt; your operation or &lt;font color='red'&gt;remove&lt;/font&gt; a conflicting constraint.&lt;/b&gt;</source>
+      <translation>&lt;b&gt;La contrainte est en conflit avec d'autres. Pour y remédier, vous pouvez soit &lt;font color='red'&gt;annuler (Ctrl+Z)&lt;/font&gt; votre opération, soit &lt;font color='red'&gt;supprimer&lt;/font&gt; une contrainte conflictuelle.&lt;/b&gt;</translation>
+    </message>
   </context>
 
   <context>
       <translation>Type d&apos;angle</translation>
     </message>
     <message>
-      <source>Complementary</source>
-      <translation>Complémentaire</translation>
+      <source>Supplementary</source>
+      <translation>Supplémentaire</translation>
     </message>
     <message>
       <source>Direct</source>
     </message>
   </context>
 
+  <context>
+    <name>SketchBSpline</name>
+    <message>
+        <source>Number of B-spline poles should be 2 or more</source>
+        <translation>Le nombre de pôles B-spline doit être de 2 ou plus</translation>
+    </message>
+  </context>
+
 </TS>
index 92a0fe448c091768fe2c5f96d65958a12974205e..7f94403922999c3327628b4d166ad69d5127ccb5 100644 (file)
@@ -61,3 +61,5 @@ if __name__ == "__main__":
     Events_Loop.loop().flush(event);
 
     assert(listener.myEventProcessed)
+    # explicitly remove the listener to improve code coverage
+    listener.__del__()
index 4fb18ea66eb4fedbac1cf831f38e230a1c6d7349..632b73fb039d7847cdb3dfcea7a7679eb9d57c68 100644 (file)
@@ -64,3 +64,5 @@ if __name__ == "__main__":
     Events_Loop.loop().flush(event);
 
     assert(listener.myEventProcessed)
+    # explicitly remove the listener to improve code coverage
+    listener.__del__()
diff --git a/src/SketchPlugin/Test/Test3132.py b/src/SketchPlugin/Test/Test3132.py
new file mode 100644 (file)
index 0000000..7dbba32
--- /dev/null
@@ -0,0 +1,67 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+from GeomAPI import *
+import math
+
+ELL_CENTER_X = 10
+ELL_CENTER_Y = 10
+ELL_MAJOR_RAD = 30
+ELL_MINOR_RAD = 15
+DOF_1 = 5
+DOF_2 = 9
+
+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"))
+SketchEllipse_1 = Sketch_1.addEllipse(ELL_CENTER_X, ELL_CENTER_Y, ELL_CENTER_X + math.sqrt(ELL_MAJOR_RAD**2 + ELL_MINOR_RAD**2), ELL_CENTER_Y, ELL_MINOR_RAD)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchLine_1, SketchLine_2] = SketchEllipse_1.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+model.do()
+assert(model.dof(Sketch_1) == DOF_1)
+
+# trim the ellipse
+ANGLE = math.pi/4
+Sketch_1.addTrim(SketchEllipse_1.feature(), GeomAPI_Pnt2d(ELL_CENTER_X + ELL_MAJOR_RAD * math.cos(ANGLE), ELL_CENTER_Y + ELL_MINOR_RAD * math.sin(ANGLE)))
+model.do()
+assert(model.dof(Sketch_1) == DOF_1)
+
+Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchEllipse_2 = Sketch_2.addEllipse(ELL_CENTER_X, ELL_CENTER_Y, ELL_CENTER_X + math.sqrt(ELL_MAJOR_RAD**2 + ELL_MINOR_RAD**2), ELL_CENTER_Y, ELL_MINOR_RAD)
+[SketchPoint_8, SketchPoint_9, SketchPoint_10, SketchPoint_11, SketchPoint_12, SketchPoint_13, SketchPoint_14, SketchLine_3, SketchLine_4] = SketchEllipse_2.construction(center = "aux", firstFocus = "aux", secondFocus = "aux", majorAxisStart = "aux", majorAxisEnd = "aux", minorAxisStart = "aux", minorAxisEnd = "aux", majorAxis = "aux", minorAxis = "aux")
+SketchLine_5 = Sketch_2.addLine(15.23538168732762, 24.77570901315218, 37.44845404222143, 43.05543771157006)
+SketchConstraintCoincidence_3 = Sketch_2.setCoincident(SketchLine_5.startPoint(), SketchEllipse_2.result())
+SketchLine_6 = Sketch_2.addLine(37.44845404222143, 43.05543771157006, 37.66137837703927, 15.83721541173749)
+SketchConstraintCoincidence_4 = Sketch_2.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint())
+SketchConstraintCoincidence_5 = Sketch_2.setCoincident(SketchLine_6.endPoint(), SketchEllipse_2.result())
+model.do()
+assert(model.dof(Sketch_2) == DOF_2)
+
+# split the ellipse
+Sketch_2.addSplit(SketchEllipse_2.feature(), GeomAPI_Pnt2d(ELL_CENTER_X + ELL_MAJOR_RAD * math.cos(ANGLE), ELL_CENTER_Y + ELL_MINOR_RAD * math.sin(ANGLE)))
+DOF_2 += 3
+model.do()
+assert(model.dof(Sketch_2) == DOF_2)
+
+
+model.end()
+
+assert(model.checkPythonDump())
index 279b50f949fd5be7a51c09bc178f080f575c2460..336e078423c5ac1907cab5cdcdf7578f3bfd91be 100644 (file)
@@ -28,6 +28,7 @@
 #=========================================================================
 # of the test
 #=========================================================================
+from GeomAPI import *
 from GeomDataAPI import *
 from ModelAPI import *
 import math
diff --git a/src/SketchPlugin/Test/TestBSplineAddPole.py b/src/SketchPlugin/Test/TestBSplineAddPole.py
new file mode 100644 (file)
index 0000000..09a577c
--- /dev/null
@@ -0,0 +1,116 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test for adding a pole to already created B-spline curve
+"""
+
+from salome.shaper import model
+from GeomAPI import *
+import random
+
+TOLERANCE = 1.e-7
+
+def assertSubFeatures(theSketch, theNbPoints, theNbLines, theNbSplines, theNbSplinesP, theNbCoincidences, theNbInternal):
+    model.testNbSubFeatures(theSketch, "SketchPoint", theNbPoints)
+    model.testNbSubFeatures(theSketch, "SketchLine", theNbLines)
+    model.testNbSubFeatures(theSketch, "SketchBSpline", theNbSplines)
+    model.testNbSubFeatures(theSketch, "SketchBSplinePeriodic", theNbSplinesP)
+    model.testNbSubFeatures(theSketch, "SketchConstraintCoincidence", theNbCoincidences)
+    model.testNbSubFeatures(theSketch, "SketchConstraintCoincidenceInternal", theNbInternal)
+
+def assertPoles(thePoles, theReference):
+    assert(thePoles.size() == len(theReference))
+    for ind in range(0, len(theReference)):
+        pole = thePoles.pnt(ind)
+        ref = GeomAPI_Pnt2d(theReference[ind][0], theReference[ind][1])
+        assert(model.distancePointPoint(pole, ref) < TOLERANCE), "Index = {}, pole = ({}, {}), refdata = ({}, {})".format(ind, pole.x(), pole.y(), ref.x(), ref.y())
+
+
+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"))
+
+SketchBSpline_1_poles = [(-25, 5), (-15, 35), (15, 35), (28, 5)]
+SketchBSpline_1 = Sketch_1.addSpline(poles = SketchBSpline_1_poles)
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4] = SketchBSpline_1.controlPoles(auxiliary = [0, 1, 2, 3])
+[SketchLine_1, SketchLine_2, SketchLine_3] = SketchBSpline_1.controlPolygon(auxiliary = [0, 1, 2])
+
+SketchBSplinePeriodic_1_poles = [(-20, -10), (20, -40), (20, -10), (-20, -40)]
+SketchBSplinePeriodic_1 = Sketch_1.addSpline(poles = SketchBSplinePeriodic_1_poles, periodic = True)
+[SketchPoint_5, SketchPoint_6, SketchPoint_7, SketchPoint_8] = SketchBSplinePeriodic_1.controlPoles(auxiliary = [0, 1, 2, 3])
+[SketchLine_4, SketchLine_5, SketchLine_6, SketchLine_7] = SketchBSplinePeriodic_1.controlPolygon(auxiliary = [0, 1, 2, 3])
+model.do()
+
+# check original values
+NBPOINTS = 8
+NBLINES = 7
+NBSPLINES = 1
+NBSPLINESPERIODIC = 1
+NBCOINCIDENCES = 0
+NBINTERNAL = 22
+assertSubFeatures(Sketch_1, NBPOINTS, NBLINES, NBSPLINES, NBSPLINESPERIODIC, NBCOINCIDENCES, NBINTERNAL)
+assertPoles(SketchBSpline_1.poles(), SketchBSpline_1_poles)
+assertPoles(SketchBSplinePeriodic_1.poles(), SketchBSplinePeriodic_1_poles)
+
+# add poles to non-periodic B-spline
+ind = 0
+while ind < len(SketchBSpline_1_poles):
+    x = random.uniform(-25, 25)
+    y = random.uniform(5, 40)
+    SketchBSpline_1.insertPole(ind, GeomAPI_Pnt2d(x, y))
+    if ind + 1 < len(SketchBSpline_1_poles):
+        SketchBSpline_1_poles.insert(ind + 1, (x, y))
+    else:
+        SketchBSpline_1_poles.insert(len(SketchBSpline_1_poles) - 1, (x, y))
+    ind += 2
+    model.do()
+
+    NBPOINTS += 1
+    NBLINES += 1
+    NBINTERNAL += 3
+    assertSubFeatures(Sketch_1, NBPOINTS, NBLINES, NBSPLINES, NBSPLINESPERIODIC, NBCOINCIDENCES, NBINTERNAL)
+    assertPoles(SketchBSpline_1.poles(), SketchBSpline_1_poles)
+
+# add poles to periodic B-spline
+ind = 0
+while ind < len(SketchBSplinePeriodic_1_poles):
+    x = random.uniform(-25, 25)
+    y = random.uniform(-45, -5)
+    SketchBSplinePeriodic_1.insertPole(ind, GeomAPI_Pnt2d(x, y))
+    SketchBSplinePeriodic_1_poles.insert(ind + 1, (x, y))
+    ind += 2
+    model.do()
+
+    NBPOINTS += 1
+    NBLINES += 1
+    NBINTERNAL += 3
+    assertSubFeatures(Sketch_1, NBPOINTS, NBLINES, NBSPLINES, NBSPLINESPERIODIC, NBCOINCIDENCES, NBINTERNAL)
+    assertPoles(SketchBSplinePeriodic_1.poles(), SketchBSplinePeriodic_1_poles)
+
+model.end()
+
+# check error on incorrect action
+model.begin()
+assert(not SketchBSpline_1.feature().customAction("wrong_action"))
+model.end()
+
+#assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngleBehaviorBackward_1.py b/src/SketchPlugin/Test/TestConstraintAngleBehaviorBackward_1.py
new file mode 100644 (file)
index 0000000..086b779
--- /dev/null
@@ -0,0 +1,113 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2019-12-12"
+
+ANGLE_DIRECT = 0
+ANGLE_COMPLEMENTARY = 1
+ANGLE_BACKWARD = 2
+
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchCommonFeature = aDocument.addFeature("Sketch")
+aSketchFeature = featureToCompositeFeature(aSketchCommonFeature)
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Create two lines
+#=========================================================================
+aSession.startOperation()
+aSketchLineA = aSketchFeature.addFeature("SketchLine")
+aStartPointA = geomDataAPI_Point2D(aSketchLineA.attribute("StartPoint"))
+aEndPointA = geomDataAPI_Point2D(aSketchLineA.attribute("EndPoint"))
+aStartPointA.setValue(-100., 25.)
+aEndPointA.setValue(100., -25.)
+
+aSketchLineB = aSketchFeature.addFeature("SketchLine")
+aStartPointB = geomDataAPI_Point2D(aSketchLineB.attribute("StartPoint"))
+aEndPointB = geomDataAPI_Point2D(aSketchLineB.attribute("EndPoint"))
+aStartPointB.setValue(-100., -25.)
+aEndPointB.setValue(100., 25.)
+aSession.finishOperation()
+#=========================================================================
+# Make a constraint to keep the angle
+#=========================================================================
+ANGLE_DEGREE = 330.
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintAngle")
+anAngleType = aConstraint.integer("AngleType")
+anAngleType.setValue(ANGLE_DIRECT)
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointA")).setValue(aStartPointA.pnt())
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointB")).setValue(aStartPointB.pnt())
+aConstraint.refattr("ConstraintEntityA").setObject(aSketchLineA.firstResult())
+aConstraint.refattr("ConstraintEntityB").setObject(aSketchLineB.firstResult())
+anAngleVal = aConstraint.real("AngleValue")
+anAngleVal.setValue(360. - ANGLE_DEGREE)
+aSession.finishOperation()
+#=========================================================================
+# Change the type of the constraint
+#=========================================================================
+aSession.startOperation()
+anAngleType.setValue(ANGLE_BACKWARD)
+aSession.finishOperation()
+#=========================================================================
+# Move presentation of the angle and check the angle value
+#=========================================================================
+pointsA = [aStartPointA.pnt(), aEndPointA.pnt()]
+pointsB = [aStartPointB.pnt(), aEndPointB.pnt()]
+refs = [(pointsA[0], pointsB[1], 540. - ANGLE_DEGREE),
+        (pointsA[1], pointsB[1], ANGLE_DEGREE),
+        (pointsA[1], pointsB[0], 540. - ANGLE_DEGREE),
+        (pointsA[0], pointsB[0], ANGLE_DEGREE)
+       ]
+aFlyoutPoint = geomDataAPI_Point2D(aConstraint.attribute("ConstraintFlyoutValuePnt"))
+for ref in refs:
+    aSession.startOperation()
+    aFlyoutPoint.setValue(0.5 * (ref[0].x() + ref[1].x()), 0.5 * (ref[0].y() + ref[1].y()))
+    aSession.finishOperation()
+    assert(anAngleType.value() == ANGLE_BACKWARD)
+    assert(anAngleVal.value() == ref[2])
+    assert(aStartPointA.x() == pointsA[0].x() and aStartPointA.y() == pointsA[0].y())
+    assert(aEndPointA.x() == pointsA[1].x() and aEndPointA.y() == pointsA[1].y())
+    assert(aStartPointB.x() == pointsB[0].x() and aStartPointB.y() == pointsB[0].y())
+    assert(aEndPointB.x() == pointsB[1].x() and aEndPointB.y() == pointsB[1].y())
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngleBehaviorBackward_2.py b/src/SketchPlugin/Test/TestConstraintAngleBehaviorBackward_2.py
new file mode 100644 (file)
index 0000000..f646109
--- /dev/null
@@ -0,0 +1,107 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2019-12-12"
+
+ANGLE_DIRECT = 0
+ANGLE_COMPLEMENTARY = 1
+ANGLE_BACKWARD = 2
+
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchCommonFeature = aDocument.addFeature("Sketch")
+aSketchFeature = featureToCompositeFeature(aSketchCommonFeature)
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Create two lines
+#=========================================================================
+aSession.startOperation()
+aSketchLineA = aSketchFeature.addFeature("SketchLine")
+aStartPointA = geomDataAPI_Point2D(aSketchLineA.attribute("StartPoint"))
+aEndPointA = geomDataAPI_Point2D(aSketchLineA.attribute("EndPoint"))
+aStartPointA.setValue(-100., 25.)
+aEndPointA.setValue(100., -25.)
+
+aSketchLineB = aSketchFeature.addFeature("SketchLine")
+aStartPointB = geomDataAPI_Point2D(aSketchLineB.attribute("StartPoint"))
+aEndPointB = geomDataAPI_Point2D(aSketchLineB.attribute("EndPoint"))
+aStartPointB.setValue(-100., -25.)
+aEndPointB.setValue(100., 25.)
+aSession.finishOperation()
+#=========================================================================
+# Make a constraint to keep the angle
+#=========================================================================
+ANGLE_DEGREE = 330.
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintAngle")
+anAngleType = aConstraint.integer("AngleType")
+anAngleType.setValue(ANGLE_BACKWARD)
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointA")).setValue(aStartPointA.pnt())
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointB")).setValue(aStartPointB.pnt())
+aConstraint.refattr("ConstraintEntityA").setObject(aSketchLineA.firstResult())
+aConstraint.refattr("ConstraintEntityB").setObject(aSketchLineB.firstResult())
+anAngleVal = aConstraint.real("AngleValue")
+anAngleVal.setValue(ANGLE_DEGREE)
+aSession.finishOperation()
+#=========================================================================
+# Move presentation of the angle and check the angle value
+#=========================================================================
+pointsA = [aStartPointA.pnt(), aEndPointA.pnt()]
+pointsB = [aStartPointB.pnt(), aEndPointB.pnt()]
+refs = [(pointsA[0], pointsB[1], 540. - ANGLE_DEGREE),
+        (pointsA[1], pointsB[1], ANGLE_DEGREE),
+        (pointsA[1], pointsB[0], 540. - ANGLE_DEGREE),
+        (pointsA[0], pointsB[0], ANGLE_DEGREE)
+       ]
+aFlyoutPoint = geomDataAPI_Point2D(aConstraint.attribute("ConstraintFlyoutValuePnt"))
+for ref in refs:
+    aSession.startOperation()
+    aFlyoutPoint.setValue(0.5 * (ref[0].x() + ref[1].x()), 0.5 * (ref[0].y() + ref[1].y()))
+    aSession.finishOperation()
+    assert(anAngleType.value() == ANGLE_BACKWARD)
+    assert(anAngleVal.value() == ref[2])
+    assert(aStartPointA.x() == pointsA[0].x() and aStartPointA.y() == pointsA[0].y())
+    assert(aEndPointA.x() == pointsA[1].x() and aEndPointA.y() == pointsA[1].y())
+    assert(aStartPointB.x() == pointsB[0].x() and aStartPointB.y() == pointsB[0].y())
+    assert(aEndPointB.x() == pointsB[1].x() and aEndPointB.y() == pointsB[1].y())
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngleBehaviorDirect.py b/src/SketchPlugin/Test/TestConstraintAngleBehaviorDirect.py
new file mode 100644 (file)
index 0000000..51c10dd
--- /dev/null
@@ -0,0 +1,107 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2019-12-12"
+
+ANGLE_DIRECT = 0
+ANGLE_COMPLEMENTARY = 1
+ANGLE_BACKWARD = 2
+
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchCommonFeature = aDocument.addFeature("Sketch")
+aSketchFeature = featureToCompositeFeature(aSketchCommonFeature)
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Create two lines
+#=========================================================================
+aSession.startOperation()
+aSketchLineA = aSketchFeature.addFeature("SketchLine")
+aStartPointA = geomDataAPI_Point2D(aSketchLineA.attribute("StartPoint"))
+aEndPointA = geomDataAPI_Point2D(aSketchLineA.attribute("EndPoint"))
+aStartPointA.setValue(-100., 25.)
+aEndPointA.setValue(100., -25.)
+
+aSketchLineB = aSketchFeature.addFeature("SketchLine")
+aStartPointB = geomDataAPI_Point2D(aSketchLineB.attribute("StartPoint"))
+aEndPointB = geomDataAPI_Point2D(aSketchLineB.attribute("EndPoint"))
+aStartPointB.setValue(-100., -25.)
+aEndPointB.setValue(100., 25.)
+aSession.finishOperation()
+#=========================================================================
+# Make a constraint to keep the angle
+#=========================================================================
+ANGLE_DEGREE = 30.
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintAngle")
+anAngleType = aConstraint.integer("AngleType")
+anAngleType.setValue(ANGLE_DIRECT)
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointA")).setValue(aStartPointA.pnt())
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointB")).setValue(aStartPointB.pnt())
+aConstraint.refattr("ConstraintEntityA").setObject(aSketchLineA.firstResult())
+aConstraint.refattr("ConstraintEntityB").setObject(aSketchLineB.firstResult())
+anAngleVal = aConstraint.real("AngleValue")
+anAngleVal.setValue(ANGLE_DEGREE)
+aSession.finishOperation()
+#=========================================================================
+# Move presentation of the angle and check the angle value
+#=========================================================================
+pointsA = [aStartPointA.pnt(), aEndPointA.pnt()]
+pointsB = [aStartPointB.pnt(), aEndPointB.pnt()]
+refs = [(pointsA[0], pointsB[1], 180. - ANGLE_DEGREE),
+        (pointsA[1], pointsB[1], ANGLE_DEGREE),
+        (pointsA[1], pointsB[0], 180. - ANGLE_DEGREE),
+        (pointsA[0], pointsB[0], ANGLE_DEGREE)
+       ]
+aFlyoutPoint = geomDataAPI_Point2D(aConstraint.attribute("ConstraintFlyoutValuePnt"))
+for ref in refs:
+    aSession.startOperation()
+    aFlyoutPoint.setValue(0.5 * (ref[0].x() + ref[1].x()), 0.5 * (ref[0].y() + ref[1].y()))
+    aSession.finishOperation()
+    assert(anAngleType.value() == ANGLE_DIRECT)
+    assert(anAngleVal.value() == ref[2])
+    assert(aStartPointA.x() == pointsA[0].x() and aStartPointA.y() == pointsA[0].y())
+    assert(aEndPointA.x() == pointsA[1].x() and aEndPointA.y() == pointsA[1].y())
+    assert(aStartPointB.x() == pointsB[0].x() and aStartPointB.y() == pointsB[0].y())
+    assert(aEndPointB.x() == pointsB[1].x() and aEndPointB.y() == pointsB[1].y())
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngleBehaviorSupplementary_1.py b/src/SketchPlugin/Test/TestConstraintAngleBehaviorSupplementary_1.py
new file mode 100644 (file)
index 0000000..542ebff
--- /dev/null
@@ -0,0 +1,113 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2019-12-12"
+
+ANGLE_DIRECT = 0
+ANGLE_COMPLEMENTARY = 1
+ANGLE_BACKWARD = 2
+
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchCommonFeature = aDocument.addFeature("Sketch")
+aSketchFeature = featureToCompositeFeature(aSketchCommonFeature)
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Create two lines
+#=========================================================================
+aSession.startOperation()
+aSketchLineA = aSketchFeature.addFeature("SketchLine")
+aStartPointA = geomDataAPI_Point2D(aSketchLineA.attribute("StartPoint"))
+aEndPointA = geomDataAPI_Point2D(aSketchLineA.attribute("EndPoint"))
+aStartPointA.setValue(-100., 25.)
+aEndPointA.setValue(100., -25.)
+
+aSketchLineB = aSketchFeature.addFeature("SketchLine")
+aStartPointB = geomDataAPI_Point2D(aSketchLineB.attribute("StartPoint"))
+aEndPointB = geomDataAPI_Point2D(aSketchLineB.attribute("EndPoint"))
+aStartPointB.setValue(-100., -25.)
+aEndPointB.setValue(100., 25.)
+aSession.finishOperation()
+#=========================================================================
+# Make a constraint to keep the angle
+#=========================================================================
+ANGLE_DEGREE = 30.
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintAngle")
+anAngleType = aConstraint.integer("AngleType")
+anAngleType.setValue(ANGLE_DIRECT)
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointA")).setValue(aStartPointA.pnt())
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointB")).setValue(aStartPointB.pnt())
+aConstraint.refattr("ConstraintEntityA").setObject(aSketchLineA.firstResult())
+aConstraint.refattr("ConstraintEntityB").setObject(aSketchLineB.firstResult())
+anAngleVal = aConstraint.real("AngleValue")
+anAngleVal.setValue(ANGLE_DEGREE)
+aSession.finishOperation()
+#=========================================================================
+# Change the type of the constraint
+#=========================================================================
+aSession.startOperation()
+anAngleType.setValue(ANGLE_COMPLEMENTARY)
+aSession.finishOperation()
+#=========================================================================
+# Move presentation of the angle and check the angle value
+#=========================================================================
+pointsA = [aStartPointA.pnt(), aEndPointA.pnt()]
+pointsB = [aStartPointB.pnt(), aEndPointB.pnt()]
+refs = [(pointsA[0], pointsB[0], ANGLE_DEGREE),
+        (pointsA[0], pointsB[1], 180. - ANGLE_DEGREE),
+        (pointsA[1], pointsB[1], ANGLE_DEGREE),
+        (pointsA[1], pointsB[0], 180. - ANGLE_DEGREE),
+       ]
+aFlyoutPoint = geomDataAPI_Point2D(aConstraint.attribute("ConstraintFlyoutValuePnt"))
+for ref in refs:
+    aSession.startOperation()
+    aFlyoutPoint.setValue(0.5 * (ref[0].x() + ref[1].x()), 0.5 * (ref[0].y() + ref[1].y()))
+    aSession.finishOperation()
+    assert(anAngleType.value() == ANGLE_COMPLEMENTARY)
+    assert(anAngleVal.value() == ref[2])
+    assert(aStartPointA.x() == pointsA[0].x() and aStartPointA.y() == pointsA[0].y())
+    assert(aEndPointA.x() == pointsA[1].x() and aEndPointA.y() == pointsA[1].y())
+    assert(aStartPointB.x() == pointsB[0].x() and aStartPointB.y() == pointsB[0].y())
+    assert(aEndPointB.x() == pointsB[1].x() and aEndPointB.y() == pointsB[1].y())
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngleBehaviorSupplementary_2.py b/src/SketchPlugin/Test/TestConstraintAngleBehaviorSupplementary_2.py
new file mode 100644 (file)
index 0000000..1d431da
--- /dev/null
@@ -0,0 +1,107 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2019-12-12"
+
+ANGLE_DIRECT = 0
+ANGLE_COMPLEMENTARY = 1
+ANGLE_BACKWARD = 2
+
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchCommonFeature = aDocument.addFeature("Sketch")
+aSketchFeature = featureToCompositeFeature(aSketchCommonFeature)
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Create two lines
+#=========================================================================
+aSession.startOperation()
+aSketchLineA = aSketchFeature.addFeature("SketchLine")
+aStartPointA = geomDataAPI_Point2D(aSketchLineA.attribute("StartPoint"))
+aEndPointA = geomDataAPI_Point2D(aSketchLineA.attribute("EndPoint"))
+aStartPointA.setValue(-100., 25.)
+aEndPointA.setValue(100., -25.)
+
+aSketchLineB = aSketchFeature.addFeature("SketchLine")
+aStartPointB = geomDataAPI_Point2D(aSketchLineB.attribute("StartPoint"))
+aEndPointB = geomDataAPI_Point2D(aSketchLineB.attribute("EndPoint"))
+aStartPointB.setValue(-100., -25.)
+aEndPointB.setValue(100., 25.)
+aSession.finishOperation()
+#=========================================================================
+# Make a constraint to keep the angle
+#=========================================================================
+ANGLE_DEGREE = 30.
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintAngle")
+anAngleType = aConstraint.integer("AngleType")
+anAngleType.setValue(ANGLE_COMPLEMENTARY)
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointA")).setValue(aStartPointA.pnt())
+geomDataAPI_Point2D(aConstraint.attribute("SelectedPointB")).setValue(aStartPointB.pnt())
+aConstraint.refattr("ConstraintEntityA").setObject(aSketchLineA.firstResult())
+aConstraint.refattr("ConstraintEntityB").setObject(aSketchLineB.firstResult())
+anAngleVal = aConstraint.real("AngleValue")
+anAngleVal.setValue(ANGLE_DEGREE)
+aSession.finishOperation()
+#=========================================================================
+# Move presentation of the angle and check the angle value
+#=========================================================================
+pointsA = [aStartPointA.pnt(), aEndPointA.pnt()]
+pointsB = [aStartPointB.pnt(), aEndPointB.pnt()]
+refs = [(pointsA[0], pointsB[0], ANGLE_DEGREE),
+        (pointsA[0], pointsB[1], 180. - ANGLE_DEGREE),
+        (pointsA[1], pointsB[1], ANGLE_DEGREE),
+        (pointsA[1], pointsB[0], 180. - ANGLE_DEGREE),
+       ]
+aFlyoutPoint = geomDataAPI_Point2D(aConstraint.attribute("ConstraintFlyoutValuePnt"))
+for ref in refs:
+    aSession.startOperation()
+    aFlyoutPoint.setValue(0.5 * (ref[0].x() + ref[1].x()), 0.5 * (ref[0].y() + ref[1].y()))
+    aSession.finishOperation()
+    assert(anAngleType.value() == ANGLE_COMPLEMENTARY)
+    assert(anAngleVal.value() == ref[2])
+    assert(aStartPointA.x() == pointsA[0].x() and aStartPointA.y() == pointsA[0].y())
+    assert(aEndPointA.x() == pointsA[1].x() and aEndPointA.y() == pointsA[1].y())
+    assert(aStartPointB.x() == pointsB[0].x() and aStartPointB.y() == pointsB[0].y())
+    assert(aEndPointB.x() == pointsB[1].x() and aEndPointB.y() == pointsB[1].y())
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngle_v0_1.py b/src/SketchPlugin/Test/TestConstraintAngle_v0_1.py
new file mode 100644 (file)
index 0000000..ff63092
--- /dev/null
@@ -0,0 +1,261 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+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(-17.48270627886662, -77.57247534383079, 0.4653113240225388, -68.74815303974528)
+SketchLine_2 = Sketch_1.addLine(0.4653113240225388, -68.74815303974528, 8.542125432770632, -50.45156664083665)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(8.542125432770632, -50.45156664083665, -8.178735979228234, 7.171456480488104)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(-17.48270627886662, -77.57247534383079, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.endPoint())
+SketchLine_6 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.startPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(-30.39232801419296, -18.99499937550162, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.endPoint())
+SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_2.result(), 220)
+SketchConstraintAngle_2 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), 140)
+SketchConstraintAngle_3 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_4.result(), 140)
+SketchConstraintAngle_4 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_5.result(), 220)
+SketchConstraintAngle_5 = Sketch_1.setAngle(SketchLine_5.result(), SketchLine_4.result(), 220)
+SketchConstraintAngle_6 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_7.result(), 140)
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_1.result())
+SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_4.result())
+SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_5.result())
+SketchConstraintEqual_4 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_6.result())
+SketchLine_8 = Sketch_1.addLine(14.775406531722, -47.38691166633646, -20.64529817713098, 1.042146531487715)
+SketchPoint_1 = Sketch_1.addPoint(2.968504962104344, -31.24389226706174)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_3.result())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_8.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_2.endPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_8.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_3 = Sketch_1.setDistance(SketchPoint_1.coordinates(), SketchLine_3.endPoint(), 40, True)
+SketchConstraintEqual_5 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_8.result())
+SketchLine_9 = Sketch_1.addLine(19.58460105006861, -42.37517030669127, -30.26368721382418, -8.981336187802668)
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_9.result())
+SketchConstraintEqual_6 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_9.result())
+SketchConstraintDistance_4 = Sketch_1.setDistance(SketchLine_9.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintAngle_7 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_8.result(), 20)
+SketchConstraintAngle_8 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_8.result(), 340)
+SketchLine_10 = Sketch_1.addLine(3.875430505227681, -29.75334619738324, -48.69363438803513, -0.8311502875301234)
+SketchPoint_2 = Sketch_1.addPoint(-13.64759112585992, -20.1126142274322)
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_10.result())
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_9.result())
+SketchConstraintDistance_5 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_10.startPoint(), 20, True)
+SketchConstraintDistance_6 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_9.endPoint(), 20, True)
+SketchConstraintEqual_7 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(0.001529676402264532, -26.13785899518266, -31.94417752476854, -12.03580011868412)
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_11.result())
+SketchConstraintDistance_7 = Sketch_1.setDistance(SketchLine_11.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_9 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_10.result(), 185)
+SketchConstraintAngle_10 = Sketch_1.setAngle(SketchLine_11.result(), SketchLine_10.result(), 355)
+SketchLine_12 = Sketch_1.addLine(5.283311970593094, -26.56404104269606, -51.50939731876596, -7.20976059690448)
+SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_12.result())
+SketchConstraintEqual_8 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_12.result())
+SketchConstraintDistance_8 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_12.startPoint(), 20, True)
+SketchConstraintAngle_11 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_11.result(), 355)
+SketchLine_13 = Sketch_1.addLine(25.19469725542325, -29.66649476390714, -33.06873531650151, -15.33567395919474)
+SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_13.result())
+SketchConstraintEqual_9 = Sketch_1.setEqual(SketchLine_12.result(), SketchLine_13.result())
+SketchConstraintDistance_9 = Sketch_1.setDistance(SketchLine_13.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_12 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_13.result(), 185)
+SketchLine_14 = Sketch_1.addLine(25.87956617560386, -26.24481087018098, -33.41116977659181, -17.04651590605781)
+SketchConstraintCoincidence_15 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_14.result())
+SketchConstraintDistance_10 = Sketch_1.setDistance(SketchLine_14.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_13 = Sketch_1.setAngle(SketchLine_13.result(), SketchLine_14.result(), 5)
+SketchConstraintEqual_10 = Sketch_1.setEqual(SketchLine_13.result(), SketchLine_14.result())
+SketchLine_15 = Sketch_1.addLine(26.26360956157018, -22.77645725728195, -33.60319146957497, -18.78069271250733)
+SketchConstraintCoincidence_16 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_15.result())
+SketchConstraintEqual_11 = Sketch_1.setEqual(SketchLine_14.result(), SketchLine_15.result())
+SketchConstraintDistance_11 = Sketch_1.setDistance(SketchLine_15.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_14 = Sketch_1.setAngle(SketchLine_15.result(), SketchLine_14.result(), 355)
+SketchConstraintCoincidence_17 = Sketch_1.setCoincident(SketchLine_7.startPoint(), SketchLine_15.result())
+
+SketchLine_16 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 78.00643494689578, -67.38706888260469)
+SketchLine_17 = Sketch_1.addLine(78.00643494689578, -67.38706888260469, 86.19687277706721, -49.141063069)
+SketchConstraintCoincidence_18 = Sketch_1.setCoincident(SketchLine_16.endPoint(), SketchLine_17.startPoint())
+SketchLine_18 = Sketch_1.addLine(86.19687277706721, -49.141063069, 69.83467154684908, 8.584826884311665)
+SketchConstraintCoincidence_19 = Sketch_1.setCoincident(SketchLine_17.endPoint(), SketchLine_18.startPoint())
+SketchLine_19 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_20 = Sketch_1.setCoincident(SketchLine_16.startPoint(), SketchLine_19.startPoint())
+SketchLine_20 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_21 = Sketch_1.setCoincident(SketchLine_19.endPoint(), SketchLine_20.endPoint())
+SketchLine_21 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_22 = Sketch_1.setCoincident(SketchLine_20.startPoint(), SketchLine_21.startPoint())
+SketchLine_22 = Sketch_1.addLine(47.45878924889096, -17.44298467461911, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_23 = Sketch_1.setCoincident(SketchLine_21.endPoint(), SketchLine_22.endPoint())
+SketchConstraintAngle_15 = Sketch_1.setAngleComplementary(SketchLine_16.result(), SketchLine_17.result(), 40)
+SketchConstraintAngle_16 = Sketch_1.setAngleComplementary(SketchLine_18.result(), SketchLine_17.result(), 40)
+SketchConstraintAngle_17 = Sketch_1.setAngleComplementary(SketchLine_16.result(), SketchLine_19.result(), 40)
+SketchConstraintAngle_18 = Sketch_1.setAngleComplementary(SketchLine_21.result(), SketchLine_20.result(), 40)
+SketchConstraintAngle_19 = Sketch_1.setAngleComplementary(SketchLine_20.result(), SketchLine_19.result(), 40)
+SketchConstraintAngle_20 = Sketch_1.setAngleComplementary(SketchLine_21.result(), SketchLine_22.result(), 40)
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_17.result(), 20)
+SketchConstraintEqual_12 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_16.result())
+SketchConstraintEqual_13 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_19.result())
+SketchConstraintEqual_14 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_20.result())
+SketchConstraintEqual_15 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_21.result())
+SketchLine_23 = Sketch_1.addLine(92.44909133742235, -46.11522988707736, 57.3302344261388, 2.533160520466375)
+SketchPoint_3 = Sketch_1.addPoint(80.74280570032784, -29.89909975122945)
+SketchConstraintCoincidence_24 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_18.result())
+SketchConstraintCoincidence_25 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_23.result())
+SketchConstraintDistance_12 = Sketch_1.setDistance(SketchLine_17.endPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_13 = Sketch_1.setDistance(SketchLine_23.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_14 = Sketch_1.setDistance(SketchPoint_3.coordinates(), SketchLine_18.endPoint(), 40, True)
+SketchConstraintEqual_16 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_23.result())
+SketchLine_24 = Sketch_1.addLine(97.289359083568, -41.13349208617944, 47.64969893384747, -7.430315081329468)
+SketchConstraintCoincidence_26 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_24.result())
+SketchConstraintEqual_17 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_24.result())
+SketchConstraintDistance_15 = Sketch_1.setDistance(SketchLine_24.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintAngle_21 = Sketch_1.setAngleComplementary(SketchLine_18.result(), SketchLine_23.result(), 160)
+SketchConstraintAngle_22 = Sketch_1.setAngleComplementary(SketchLine_24.result(), SketchLine_23.result(), 160)
+SketchLine_25 = Sketch_1.addLine(81.65898287743471, -28.41422234660911, 29.27079119639352, 0.8343224443798635)
+SketchPoint_4 = Sketch_1.addPoint(64.19625231708766, -18.66470741627946)
+SketchConstraintCoincidence_27 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_25.result())
+SketchConstraintCoincidence_28 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_24.result())
+SketchConstraintDistance_16 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_25.startPoint(), 20, True)
+SketchConstraintDistance_17 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_24.endPoint(), 20, True)
+SketchConstraintEqual_18 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_25.result())
+SketchLine_26 = Sketch_1.addLine(95.51651619313817, -32.72404055044315, 45.95024650348294, -10.47426958610802)
+SketchConstraintCoincidence_29 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_26.result())
+SketchConstraintDistance_18 = Sketch_1.setDistance(SketchLine_26.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_23 = Sketch_1.setAngleComplementary(SketchLine_24.result(), SketchLine_25.result(), 5)
+SketchConstraintAngle_24 = Sketch_1.setAngleComplementary(SketchLine_26.result(), SketchLine_25.result(), 5)
+SketchLine_27 = Sketch_1.addLine(83.08667026246893, -25.23373396888348, 26.41541642632507, -5.526654311071399)
+SketchConstraintCoincidence_30 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_27.result())
+SketchConstraintEqual_19 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_27.result())
+SketchConstraintDistance_19 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_27.startPoint(), 20, True)
+SketchConstraintAngle_25 = Sketch_1.setAngleComplementary(SketchLine_27.result(), SketchLine_26.result(), 5)
+SketchLine_28 = Sketch_1.addLine(102.9783774975822, -28.45994944924871, 44.80518972684035, -13.76708639979482)
+SketchConstraintCoincidence_31 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_28.result())
+SketchConstraintEqual_20 = Sketch_1.setEqual(SketchLine_27.result(), SketchLine_28.result())
+SketchConstraintDistance_20 = Sketch_1.setDistance(SketchLine_28.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_26 = Sketch_1.setAngleComplementary(SketchLine_27.result(), SketchLine_28.result(), 5)
+SketchLine_29 = Sketch_1.addLine(103.6845113974033, -25.04259067061018, 44.45212277692985, -15.47576578911409)
+SketchConstraintCoincidence_32 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_29.result())
+SketchConstraintDistance_21 = Sketch_1.setDistance(SketchLine_29.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_27 = Sketch_1.setAngleComplementary(SketchLine_28.result(), SketchLine_29.result(), 175)
+SketchConstraintEqual_21 = Sketch_1.setEqual(SketchLine_28.result(), SketchLine_29.result())
+SketchLine_30 = Sketch_1.addLine(104.0901158019603, -21.57669234933498, 44.24932057465131, -17.20871494975169)
+SketchConstraintCoincidence_33 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_30.result())
+SketchConstraintEqual_22 = Sketch_1.setEqual(SketchLine_29.result(), SketchLine_30.result())
+SketchConstraintDistance_22 = Sketch_1.setDistance(SketchLine_30.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_28 = Sketch_1.setAngleComplementary(SketchLine_30.result(), SketchLine_29.result(), 175)
+SketchConstraintCoincidence_34 = Sketch_1.setCoincident(SketchLine_22.startPoint(), SketchLine_30.result())
+
+SketchLine_31 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 156.7284871354776, -69.4178124771163)
+SketchLine_32 = Sketch_1.addLine(156.7284871354776, -69.4178124771163, 164.8053019156854, -51.12122637461573)
+SketchConstraintCoincidence_35 = Sketch_1.setCoincident(SketchLine_31.endPoint(), SketchLine_32.startPoint())
+SketchLine_33 = Sketch_1.addLine(164.8053019156854, -51.12122637461573, 148.0844426183728, 6.501797360341752)
+SketchConstraintCoincidence_36 = Sketch_1.setCoincident(SketchLine_32.endPoint(), SketchLine_33.startPoint())
+SketchLine_34 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_37 = Sketch_1.setCoincident(SketchLine_31.startPoint(), SketchLine_34.startPoint())
+SketchLine_35 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_38 = Sketch_1.setCoincident(SketchLine_34.endPoint(), SketchLine_35.endPoint())
+SketchLine_36 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_39 = Sketch_1.setCoincident(SketchLine_35.startPoint(), SketchLine_36.startPoint())
+SketchLine_37 = Sketch_1.addLine(125.8708496231354, -19.66465768043951, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_40 = Sketch_1.setCoincident(SketchLine_36.endPoint(), SketchLine_37.endPoint())
+SketchConstraintAngle_29 = Sketch_1.setAngleBackward(SketchLine_31.result(), SketchLine_32.result(), 140)
+SketchConstraintAngle_30 = Sketch_1.setAngleBackward(SketchLine_33.result(), SketchLine_32.result(), 220)
+SketchConstraintAngle_31 = Sketch_1.setAngleBackward(SketchLine_31.result(), SketchLine_34.result(), 220)
+SketchConstraintAngle_32 = Sketch_1.setAngleBackward(SketchLine_36.result(), SketchLine_35.result(), 140)
+SketchConstraintAngle_33 = Sketch_1.setAngleBackward(SketchLine_35.result(), SketchLine_34.result(), 140)
+SketchConstraintAngle_34 = Sketch_1.setAngleBackward(SketchLine_36.result(), SketchLine_37.result(), 220)
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_32.result(), 20)
+SketchConstraintEqual_23 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_31.result())
+SketchConstraintEqual_24 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_34.result())
+SketchConstraintEqual_25 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_35.result())
+SketchConstraintEqual_26 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_36.result())
+SketchLine_38 = Sketch_1.addLine(171.0385831271055, -48.05657162886843, 135.6178801955327, 0.3724878688471688)
+SketchPoint_5 = Sketch_1.addPoint(159.2316821499145, -31.91355179629657)
+SketchConstraintCoincidence_41 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_33.result())
+SketchConstraintCoincidence_42 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_38.result())
+SketchConstraintDistance_23 = Sketch_1.setDistance(SketchLine_32.endPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_24 = Sketch_1.setDistance(SketchLine_38.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_25 = Sketch_1.setDistance(SketchPoint_5.coordinates(), SketchLine_33.endPoint(), 40, True)
+SketchConstraintEqual_27 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_38.result())
+SketchLine_39 = Sketch_1.addLine(175.8477778293761, -43.04483044571408, 125.9994907909914, -9.650994497461493)
+SketchConstraintCoincidence_43 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_39.result())
+SketchConstraintEqual_28 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_39.result())
+SketchConstraintDistance_26 = Sketch_1.setDistance(SketchLine_39.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintAngle_35 = Sketch_1.setAngleBackward(SketchLine_33.result(), SketchLine_38.result(), 340)
+SketchConstraintAngle_36 = Sketch_1.setAngleBackward(SketchLine_39.result(), SketchLine_38.result(), 20)
+SketchLine_40 = Sketch_1.addLine(160.1386077477389, -30.42300575990098, 107.5695439158812, -1.500807920835093)
+SketchPoint_6 = Sketch_1.addPoint(142.615586470453, -20.78227314687903)
+SketchConstraintCoincidence_44 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_40.result())
+SketchConstraintCoincidence_45 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_39.result())
+SketchConstraintDistance_27 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_40.startPoint(), 20, True)
+SketchConstraintDistance_28 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_39.endPoint(), 20, True)
+SketchConstraintEqual_29 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_40.result())
+SketchLine_41 = Sketch_1.addLine(147.1612701207084, -22.78891223719074, 124.3190003679524, -12.70545836667124)
+SketchConstraintCoincidence_46 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_41.result())
+SketchConstraintDistance_29 = Sketch_1.setDistance(SketchLine_41.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_37 = Sketch_1.setAngleBackward(SketchLine_39.result(), SketchLine_40.result(), 175)
+SketchConstraintAngle_38 = Sketch_1.setAngleBackward(SketchLine_41.result(), SketchLine_40.result(), 5)
+SketchLine_42 = Sketch_1.addLine(161.5464893301474, -27.23370065688114, 104.7537807510641, -7.879418126874779)
+SketchConstraintCoincidence_47 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_42.result())
+SketchConstraintEqual_30 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_42.result())
+SketchConstraintDistance_30 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_42.startPoint(), 20, True)
+SketchConstraintAngle_39 = Sketch_1.setAngleBackward(SketchLine_42.result(), SketchLine_41.result(), 5)
+SketchLine_43 = Sketch_1.addLine(181.4578745011218, -30.33615510881286, 123.1944424551186, -16.0053321659121)
+SketchConstraintCoincidence_48 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_43.result())
+SketchConstraintEqual_31 = Sketch_1.setEqual(SketchLine_42.result(), SketchLine_43.result())
+SketchConstraintDistance_31 = Sketch_1.setDistance(SketchLine_43.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_40 = Sketch_1.setAngleBackward(SketchLine_42.result(), SketchLine_43.result(), 175)
+SketchLine_44 = Sketch_1.addLine(182.1427435468735, -26.91447124022047, 122.8520079322427, -17.71617410020831)
+SketchConstraintCoincidence_49 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_44.result())
+SketchConstraintDistance_32 = Sketch_1.setDistance(SketchLine_44.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_41 = Sketch_1.setAngleBackward(SketchLine_43.result(), SketchLine_44.result(), 355)
+SketchConstraintEqual_32 = Sketch_1.setEqual(SketchLine_43.result(), SketchLine_44.result())
+SketchLine_45 = Sketch_1.addLine(182.5267870601237, -23.44611764141531, 122.6599861756176, -19.45035089961089)
+SketchConstraintCoincidence_50 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_45.result())
+SketchConstraintEqual_33 = Sketch_1.setEqual(SketchLine_44.result(), SketchLine_45.result())
+SketchConstraintDistance_33 = Sketch_1.setDistance(SketchLine_45.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_42 = Sketch_1.setAngleBackward(SketchLine_45.result(), SketchLine_44.result(), 5)
+SketchConstraintCoincidence_51 = Sketch_1.setCoincident(SketchLine_37.startPoint(), SketchLine_45.result())
+model.do()
+
+Extrusion_1_objects = [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_9f-SketchLine_15f-SketchLine_7f-SketchLine_6r-SketchLine_5f-SketchLine_4r"), model.selection("FACE", "Sketch_1/Face-SketchLine_16f-SketchLine_17f-SketchLine_18f-SketchLine_24f-SketchLine_30f-SketchLine_22f-SketchLine_21r-SketchLine_20f-SketchLine_19r"), model.selection("FACE", "Sketch_1/Face-SketchLine_31f-SketchLine_32f-SketchLine_33f-SketchLine_39f-SketchLine_45f-SketchLine_37f-SketchLine_36r-SketchLine_35f-SketchLine_34r")]
+Extrusion_1 = model.addExtrusion(Part_1_doc, Extrusion_1_objects, model.selection(), 10, 0)
+
+model.end()
+
+from GeomAPI import *
+
+REF_VOLUME = 25018.7130187615
+
+model.testNbResults(Extrusion_1, 3)
+model.testNbSubResults(Extrusion_1, [0, 0, 0])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.SOLID, [1, 1, 1])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.FACE, [11, 11, 11])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.EDGE, [54, 54, 54])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.VERTEX, [108, 108, 108])
+model.testResultsVolumes(Extrusion_1, [REF_VOLUME, REF_VOLUME, REF_VOLUME])
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngle_v0_2.py b/src/SketchPlugin/Test/TestConstraintAngle_v0_2.py
new file mode 100644 (file)
index 0000000..5aeaf5f
--- /dev/null
@@ -0,0 +1,263 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+model.addParameter(Part_1_doc, "Ang", "140")
+model.addParameter(Part_1_doc, "dAng", "5")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(-17.48270627886662, -77.57247534383079, 0.4653113240225388, -68.74815303974528)
+SketchLine_2 = Sketch_1.addLine(0.4653113240225388, -68.74815303974528, 8.542125432770632, -50.45156664083665)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(8.542125432770632, -50.45156664083665, -8.178735979228234, 7.171456480488104)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(-17.48270627886662, -77.57247534383079, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.endPoint())
+SketchLine_6 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.startPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(-30.39232801419296, -18.99499937550162, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.endPoint())
+SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_2.result(), "360-Ang")
+SketchConstraintAngle_2 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), "Ang")
+SketchConstraintAngle_3 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_4.result(), "Ang")
+SketchConstraintAngle_4 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_5.result(), "360-Ang")
+SketchConstraintAngle_5 = Sketch_1.setAngle(SketchLine_5.result(), SketchLine_4.result(), "360-Ang")
+SketchConstraintAngle_6 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_7.result(), "Ang")
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_1.result())
+SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_4.result())
+SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_5.result())
+SketchConstraintEqual_4 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_6.result())
+SketchLine_8 = Sketch_1.addLine(14.775406531722, -47.38691166633646, -20.64529817713098, 1.042146531487715)
+SketchPoint_1 = Sketch_1.addPoint(2.968504962104344, -31.24389226706174)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_3.result())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_8.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_2.endPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_8.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_3 = Sketch_1.setDistance(SketchPoint_1.coordinates(), SketchLine_3.endPoint(), 40, True)
+SketchConstraintEqual_5 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_8.result())
+SketchLine_9 = Sketch_1.addLine(19.58460105006861, -42.37517030669127, -30.26368721382418, -8.981336187802668)
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_9.result())
+SketchConstraintEqual_6 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_9.result())
+SketchConstraintDistance_4 = Sketch_1.setDistance(SketchLine_9.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintAngle_7 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_8.result(), "(180-Ang)/2")
+SketchConstraintAngle_8 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_8.result(), "360-(180-Ang)/2")
+SketchLine_10 = Sketch_1.addLine(3.875430505227681, -29.75334619738324, -48.69363438803513, -0.8311502875301234)
+SketchPoint_2 = Sketch_1.addPoint(-13.64759112585992, -20.1126142274322)
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_10.result())
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_9.result())
+SketchConstraintDistance_5 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_10.startPoint(), 20, True)
+SketchConstraintDistance_6 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_9.endPoint(), 20, True)
+SketchConstraintEqual_7 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(0.001529676402264532, -26.13785899518266, -31.94417752476854, -12.03580011868412)
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_11.result())
+SketchConstraintDistance_7 = Sketch_1.setDistance(SketchLine_11.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_9 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_10.result(), "180+dAng")
+SketchConstraintAngle_10 = Sketch_1.setAngle(SketchLine_11.result(), SketchLine_10.result(), "360-dAng")
+SketchLine_12 = Sketch_1.addLine(5.283311970593094, -26.56404104269606, -51.50939731876596, -7.20976059690448)
+SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_12.result())
+SketchConstraintEqual_8 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_12.result())
+SketchConstraintDistance_8 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_12.startPoint(), 20, True)
+SketchConstraintAngle_11 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_11.result(), "360-dAng")
+SketchLine_13 = Sketch_1.addLine(25.19469725542325, -29.66649476390714, -33.06873531650151, -15.33567395919474)
+SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_13.result())
+SketchConstraintEqual_9 = Sketch_1.setEqual(SketchLine_12.result(), SketchLine_13.result())
+SketchConstraintDistance_9 = Sketch_1.setDistance(SketchLine_13.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_12 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_13.result(), "180+dAng")
+SketchLine_14 = Sketch_1.addLine(25.87956617560386, -26.24481087018098, -33.41116977659181, -17.04651590605781)
+SketchConstraintCoincidence_15 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_14.result())
+SketchConstraintDistance_10 = Sketch_1.setDistance(SketchLine_14.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_13 = Sketch_1.setAngle(SketchLine_13.result(), SketchLine_14.result(), "dAng")
+SketchConstraintEqual_10 = Sketch_1.setEqual(SketchLine_13.result(), SketchLine_14.result())
+SketchLine_15 = Sketch_1.addLine(26.26360956157018, -22.77645725728195, -33.60319146957497, -18.78069271250733)
+SketchConstraintCoincidence_16 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_15.result())
+SketchConstraintEqual_11 = Sketch_1.setEqual(SketchLine_14.result(), SketchLine_15.result())
+SketchConstraintDistance_11 = Sketch_1.setDistance(SketchLine_15.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_14 = Sketch_1.setAngle(SketchLine_15.result(), SketchLine_14.result(), "360-dAng")
+SketchConstraintCoincidence_17 = Sketch_1.setCoincident(SketchLine_7.startPoint(), SketchLine_15.result())
+
+SketchLine_16 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 78.00643494689578, -67.38706888260469)
+SketchLine_17 = Sketch_1.addLine(78.00643494689578, -67.38706888260469, 86.19687277706721, -49.141063069)
+SketchConstraintCoincidence_18 = Sketch_1.setCoincident(SketchLine_16.endPoint(), SketchLine_17.startPoint())
+SketchLine_18 = Sketch_1.addLine(86.19687277706721, -49.141063069, 69.83467154684908, 8.584826884311665)
+SketchConstraintCoincidence_19 = Sketch_1.setCoincident(SketchLine_17.endPoint(), SketchLine_18.startPoint())
+SketchLine_19 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_20 = Sketch_1.setCoincident(SketchLine_16.startPoint(), SketchLine_19.startPoint())
+SketchLine_20 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_21 = Sketch_1.setCoincident(SketchLine_19.endPoint(), SketchLine_20.endPoint())
+SketchLine_21 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_22 = Sketch_1.setCoincident(SketchLine_20.startPoint(), SketchLine_21.startPoint())
+SketchLine_22 = Sketch_1.addLine(47.45878924889096, -17.44298467461911, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_23 = Sketch_1.setCoincident(SketchLine_21.endPoint(), SketchLine_22.endPoint())
+SketchConstraintAngle_15 = Sketch_1.setAngleComplementary(SketchLine_16.result(), SketchLine_17.result(), "180-Ang")
+SketchConstraintAngle_16 = Sketch_1.setAngleComplementary(SketchLine_18.result(), SketchLine_17.result(), "180-Ang")
+SketchConstraintAngle_17 = Sketch_1.setAngleComplementary(SketchLine_16.result(), SketchLine_19.result(), "180-Ang")
+SketchConstraintAngle_18 = Sketch_1.setAngleComplementary(SketchLine_21.result(), SketchLine_20.result(), "180-Ang")
+SketchConstraintAngle_19 = Sketch_1.setAngleComplementary(SketchLine_20.result(), SketchLine_19.result(), "180-Ang")
+SketchConstraintAngle_20 = Sketch_1.setAngleComplementary(SketchLine_21.result(), SketchLine_22.result(), "180-Ang")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_17.result(), 20)
+SketchConstraintEqual_12 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_16.result())
+SketchConstraintEqual_13 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_19.result())
+SketchConstraintEqual_14 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_20.result())
+SketchConstraintEqual_15 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_21.result())
+SketchLine_23 = Sketch_1.addLine(92.44909133742235, -46.11522988707736, 57.3302344261388, 2.533160520466375)
+SketchPoint_3 = Sketch_1.addPoint(80.74280570032784, -29.89909975122945)
+SketchConstraintCoincidence_24 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_18.result())
+SketchConstraintCoincidence_25 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_23.result())
+SketchConstraintDistance_12 = Sketch_1.setDistance(SketchLine_17.endPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_13 = Sketch_1.setDistance(SketchLine_23.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_14 = Sketch_1.setDistance(SketchPoint_3.coordinates(), SketchLine_18.endPoint(), 40, True)
+SketchConstraintEqual_16 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_23.result())
+SketchLine_24 = Sketch_1.addLine(97.289359083568, -41.13349208617944, 47.64969893384747, -7.430315081329468)
+SketchConstraintCoincidence_26 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_24.result())
+SketchConstraintEqual_17 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_24.result())
+SketchConstraintDistance_15 = Sketch_1.setDistance(SketchLine_24.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintAngle_21 = Sketch_1.setAngleComplementary(SketchLine_18.result(), SketchLine_23.result(), "90+Ang/2")
+SketchConstraintAngle_22 = Sketch_1.setAngleComplementary(SketchLine_24.result(), SketchLine_23.result(), "90+Ang/2")
+SketchLine_25 = Sketch_1.addLine(81.65898287743471, -28.41422234660911, 29.27079119639352, 0.8343224443798635)
+SketchPoint_4 = Sketch_1.addPoint(64.19625231708766, -18.66470741627946)
+SketchConstraintCoincidence_27 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_25.result())
+SketchConstraintCoincidence_28 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_24.result())
+SketchConstraintDistance_16 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_25.startPoint(), 20, True)
+SketchConstraintDistance_17 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_24.endPoint(), 20, True)
+SketchConstraintEqual_18 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_25.result())
+SketchLine_26 = Sketch_1.addLine(95.51651619313817, -32.72404055044315, 45.95024650348294, -10.47426958610802)
+SketchConstraintCoincidence_29 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_26.result())
+SketchConstraintDistance_18 = Sketch_1.setDistance(SketchLine_26.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_23 = Sketch_1.setAngleComplementary(SketchLine_24.result(), SketchLine_25.result(), "dAng")
+SketchConstraintAngle_24 = Sketch_1.setAngleComplementary(SketchLine_26.result(), SketchLine_25.result(), "dAng")
+SketchLine_27 = Sketch_1.addLine(83.08667026246893, -25.23373396888348, 26.41541642632507, -5.526654311071399)
+SketchConstraintCoincidence_30 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_27.result())
+SketchConstraintEqual_19 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_27.result())
+SketchConstraintDistance_19 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_27.startPoint(), 20, True)
+SketchConstraintAngle_25 = Sketch_1.setAngleComplementary(SketchLine_27.result(), SketchLine_26.result(), "dAng")
+SketchLine_28 = Sketch_1.addLine(102.9783774975822, -28.45994944924871, 44.80518972684035, -13.76708639979482)
+SketchConstraintCoincidence_31 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_28.result())
+SketchConstraintEqual_20 = Sketch_1.setEqual(SketchLine_27.result(), SketchLine_28.result())
+SketchConstraintDistance_20 = Sketch_1.setDistance(SketchLine_28.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_26 = Sketch_1.setAngleComplementary(SketchLine_27.result(), SketchLine_28.result(), "dAng")
+SketchLine_29 = Sketch_1.addLine(103.6845113974033, -25.04259067061018, 44.45212277692985, -15.47576578911409)
+SketchConstraintCoincidence_32 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_29.result())
+SketchConstraintDistance_21 = Sketch_1.setDistance(SketchLine_29.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_27 = Sketch_1.setAngleComplementary(SketchLine_28.result(), SketchLine_29.result(), "180-dAng")
+SketchConstraintEqual_21 = Sketch_1.setEqual(SketchLine_28.result(), SketchLine_29.result())
+SketchLine_30 = Sketch_1.addLine(104.0901158019603, -21.57669234933498, 44.24932057465131, -17.20871494975169)
+SketchConstraintCoincidence_33 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_30.result())
+SketchConstraintEqual_22 = Sketch_1.setEqual(SketchLine_29.result(), SketchLine_30.result())
+SketchConstraintDistance_22 = Sketch_1.setDistance(SketchLine_30.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_28 = Sketch_1.setAngleComplementary(SketchLine_30.result(), SketchLine_29.result(), "180-dAng")
+SketchConstraintCoincidence_34 = Sketch_1.setCoincident(SketchLine_22.startPoint(), SketchLine_30.result())
+
+SketchLine_31 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 156.7284871354776, -69.4178124771163)
+SketchLine_32 = Sketch_1.addLine(156.7284871354776, -69.4178124771163, 164.8053019156854, -51.12122637461573)
+SketchConstraintCoincidence_35 = Sketch_1.setCoincident(SketchLine_31.endPoint(), SketchLine_32.startPoint())
+SketchLine_33 = Sketch_1.addLine(164.8053019156854, -51.12122637461573, 148.0844426183728, 6.501797360341752)
+SketchConstraintCoincidence_36 = Sketch_1.setCoincident(SketchLine_32.endPoint(), SketchLine_33.startPoint())
+SketchLine_34 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_37 = Sketch_1.setCoincident(SketchLine_31.startPoint(), SketchLine_34.startPoint())
+SketchLine_35 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_38 = Sketch_1.setCoincident(SketchLine_34.endPoint(), SketchLine_35.endPoint())
+SketchLine_36 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_39 = Sketch_1.setCoincident(SketchLine_35.startPoint(), SketchLine_36.startPoint())
+SketchLine_37 = Sketch_1.addLine(125.8708496231354, -19.66465768043951, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_40 = Sketch_1.setCoincident(SketchLine_36.endPoint(), SketchLine_37.endPoint())
+SketchConstraintAngle_29 = Sketch_1.setAngleBackward(SketchLine_31.result(), SketchLine_32.result(), "Ang")
+SketchConstraintAngle_30 = Sketch_1.setAngleBackward(SketchLine_33.result(), SketchLine_32.result(), "360-Ang")
+SketchConstraintAngle_31 = Sketch_1.setAngleBackward(SketchLine_31.result(), SketchLine_34.result(), "360-Ang")
+SketchConstraintAngle_32 = Sketch_1.setAngleBackward(SketchLine_36.result(), SketchLine_35.result(), "Ang")
+SketchConstraintAngle_33 = Sketch_1.setAngleBackward(SketchLine_35.result(), SketchLine_34.result(), "Ang")
+SketchConstraintAngle_34 = Sketch_1.setAngleBackward(SketchLine_36.result(), SketchLine_37.result(), "360-Ang")
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_32.result(), 20)
+SketchConstraintEqual_23 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_31.result())
+SketchConstraintEqual_24 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_34.result())
+SketchConstraintEqual_25 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_35.result())
+SketchConstraintEqual_26 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_36.result())
+SketchLine_38 = Sketch_1.addLine(171.0385831271055, -48.05657162886843, 135.6178801955327, 0.3724878688471688)
+SketchPoint_5 = Sketch_1.addPoint(159.2316821499145, -31.91355179629657)
+SketchConstraintCoincidence_41 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_33.result())
+SketchConstraintCoincidence_42 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_38.result())
+SketchConstraintDistance_23 = Sketch_1.setDistance(SketchLine_32.endPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_24 = Sketch_1.setDistance(SketchLine_38.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_25 = Sketch_1.setDistance(SketchPoint_5.coordinates(), SketchLine_33.endPoint(), 40, True)
+SketchConstraintEqual_27 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_38.result())
+SketchLine_39 = Sketch_1.addLine(175.8477778293761, -43.04483044571408, 125.9994907909914, -9.650994497461493)
+SketchConstraintCoincidence_43 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_39.result())
+SketchConstraintEqual_28 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_39.result())
+SketchConstraintDistance_26 = Sketch_1.setDistance(SketchLine_39.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintAngle_35 = Sketch_1.setAngleBackward(SketchLine_33.result(), SketchLine_38.result(), "360-(180-Ang)/2")
+SketchConstraintAngle_36 = Sketch_1.setAngleBackward(SketchLine_39.result(), SketchLine_38.result(), "(180-Ang)/2")
+SketchLine_40 = Sketch_1.addLine(160.1386077477389, -30.42300575990098, 107.5695439158812, -1.500807920835093)
+SketchPoint_6 = Sketch_1.addPoint(142.615586470453, -20.78227314687903)
+SketchConstraintCoincidence_44 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_40.result())
+SketchConstraintCoincidence_45 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_39.result())
+SketchConstraintDistance_27 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_40.startPoint(), 20, True)
+SketchConstraintDistance_28 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_39.endPoint(), 20, True)
+SketchConstraintEqual_29 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_40.result())
+SketchLine_41 = Sketch_1.addLine(147.1612701207084, -22.78891223719074, 124.3190003679524, -12.70545836667124)
+SketchConstraintCoincidence_46 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_41.result())
+SketchConstraintDistance_29 = Sketch_1.setDistance(SketchLine_41.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_37 = Sketch_1.setAngleBackward(SketchLine_39.result(), SketchLine_40.result(), "180-dAng")
+SketchConstraintAngle_38 = Sketch_1.setAngleBackward(SketchLine_41.result(), SketchLine_40.result(), "dAng")
+SketchLine_42 = Sketch_1.addLine(161.5464893301474, -27.23370065688114, 104.7537807510641, -7.879418126874779)
+SketchConstraintCoincidence_47 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_42.result())
+SketchConstraintEqual_30 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_42.result())
+SketchConstraintDistance_30 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_42.startPoint(), 20, True)
+SketchConstraintAngle_39 = Sketch_1.setAngleBackward(SketchLine_42.result(), SketchLine_41.result(), "dAng")
+SketchLine_43 = Sketch_1.addLine(181.4578745011218, -30.33615510881286, 123.1944424551186, -16.0053321659121)
+SketchConstraintCoincidence_48 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_43.result())
+SketchConstraintEqual_31 = Sketch_1.setEqual(SketchLine_42.result(), SketchLine_43.result())
+SketchConstraintDistance_31 = Sketch_1.setDistance(SketchLine_43.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_40 = Sketch_1.setAngleBackward(SketchLine_42.result(), SketchLine_43.result(), "180-dAng")
+SketchLine_44 = Sketch_1.addLine(182.1427435468735, -26.91447124022047, 122.8520079322427, -17.71617410020831)
+SketchConstraintCoincidence_49 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_44.result())
+SketchConstraintDistance_32 = Sketch_1.setDistance(SketchLine_44.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_41 = Sketch_1.setAngleBackward(SketchLine_43.result(), SketchLine_44.result(), "360-dAng")
+SketchConstraintEqual_32 = Sketch_1.setEqual(SketchLine_43.result(), SketchLine_44.result())
+SketchLine_45 = Sketch_1.addLine(182.5267870601237, -23.44611764141531, 122.6599861756176, -19.45035089961089)
+SketchConstraintCoincidence_50 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_45.result())
+SketchConstraintEqual_33 = Sketch_1.setEqual(SketchLine_44.result(), SketchLine_45.result())
+SketchConstraintDistance_33 = Sketch_1.setDistance(SketchLine_45.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_42 = Sketch_1.setAngleBackward(SketchLine_45.result(), SketchLine_44.result(), "dAng")
+SketchConstraintCoincidence_51 = Sketch_1.setCoincident(SketchLine_37.startPoint(), SketchLine_45.result())
+model.do()
+
+Extrusion_1_objects = [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_9f-SketchLine_15f-SketchLine_7f-SketchLine_6r-SketchLine_5f-SketchLine_4r"), model.selection("FACE", "Sketch_1/Face-SketchLine_16f-SketchLine_17f-SketchLine_18f-SketchLine_24f-SketchLine_30f-SketchLine_22f-SketchLine_21r-SketchLine_20f-SketchLine_19r"), model.selection("FACE", "Sketch_1/Face-SketchLine_31f-SketchLine_32f-SketchLine_33f-SketchLine_39f-SketchLine_45f-SketchLine_37f-SketchLine_36r-SketchLine_35f-SketchLine_34r")]
+Extrusion_1 = model.addExtrusion(Part_1_doc, Extrusion_1_objects, model.selection(), 10, 0)
+
+model.end()
+
+from GeomAPI import *
+
+REF_VOLUME = 25018.7130187615
+
+model.testNbResults(Extrusion_1, 3)
+model.testNbSubResults(Extrusion_1, [0, 0, 0])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.SOLID, [1, 1, 1])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.FACE, [11, 11, 11])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.EDGE, [54, 54, 54])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.VERTEX, [108, 108, 108])
+model.testResultsVolumes(Extrusion_1, [REF_VOLUME, REF_VOLUME, REF_VOLUME])
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngle_v20191210_1.py b/src/SketchPlugin/Test/TestConstraintAngle_v20191210_1.py
new file mode 100644 (file)
index 0000000..4ddb918
--- /dev/null
@@ -0,0 +1,261 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+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(-17.48270627886662, -77.57247534383079, 0.4653113240225388, -68.74815303974528)
+SketchLine_2 = Sketch_1.addLine(0.4653113240225388, -68.74815303974528, 8.542125432770632, -50.45156664083665)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(8.542125432770632, -50.45156664083665, -8.178735979228234, 7.171456480488104)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(-17.48270627886662, -77.57247534383079, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.endPoint())
+SketchLine_6 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.startPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(-30.39232801419296, -18.99499937550162, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.endPoint())
+SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_2.result(), 140, type = "Direct")
+SketchConstraintAngle_2 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), 140, type = "Direct")
+SketchConstraintAngle_3 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_4.result(), 140, type = "Direct")
+SketchConstraintAngle_4 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_5.result(), 140, type = "Direct")
+SketchConstraintAngle_5 = Sketch_1.setAngle(SketchLine_5.result(), SketchLine_4.result(), 140, type = "Direct")
+SketchConstraintAngle_6 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_7.result(), 140, type = "Direct")
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_1.result())
+SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_4.result())
+SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_5.result())
+SketchConstraintEqual_4 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_6.result())
+SketchLine_8 = Sketch_1.addLine(14.775406531722, -47.38691166633646, -20.64529817713098, 1.042146531487715)
+SketchPoint_1 = Sketch_1.addPoint(2.968504962104344, -31.24389226706174)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_3.result())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_8.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_2.endPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_8.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_3 = Sketch_1.setDistance(SketchPoint_1.coordinates(), SketchLine_3.endPoint(), 40, True)
+SketchConstraintEqual_5 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_8.result())
+SketchLine_9 = Sketch_1.addLine(19.58460105006861, -42.37517030669127, -30.26368721382418, -8.981336187802668)
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_9.result())
+SketchConstraintEqual_6 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_9.result())
+SketchConstraintDistance_4 = Sketch_1.setDistance(SketchLine_9.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintAngle_7 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_8.result(), 20, type = "Direct")
+SketchConstraintAngle_8 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_8.result(), 20, type = "Direct")
+SketchLine_10 = Sketch_1.addLine(3.875430505227681, -29.75334619738324, -48.69363438803513, -0.8311502875301234)
+SketchPoint_2 = Sketch_1.addPoint(-13.64759112585992, -20.1126142274322)
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_10.result())
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_9.result())
+SketchConstraintDistance_5 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_10.startPoint(), 20, True)
+SketchConstraintDistance_6 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_9.endPoint(), 20, True)
+SketchConstraintEqual_7 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(0.001529676402264532, -26.13785899518266, -31.94417752476854, -12.03580011868412)
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_11.result())
+SketchConstraintDistance_7 = Sketch_1.setDistance(SketchLine_11.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_9 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_10.result(), 175, type = "Direct")
+SketchConstraintAngle_10 = Sketch_1.setAngle(SketchLine_11.result(), SketchLine_10.result(), 5, type = "Direct")
+SketchLine_12 = Sketch_1.addLine(5.283311970593094, -26.56404104269606, -51.50939731876596, -7.20976059690448)
+SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_12.result())
+SketchConstraintEqual_8 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_12.result())
+SketchConstraintDistance_8 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_12.startPoint(), 20, True)
+SketchConstraintAngle_11 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_11.result(), 5, type = "Direct")
+SketchLine_13 = Sketch_1.addLine(25.19469725542325, -29.66649476390714, -33.06873531650151, -15.33567395919474)
+SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_13.result())
+SketchConstraintEqual_9 = Sketch_1.setEqual(SketchLine_12.result(), SketchLine_13.result())
+SketchConstraintDistance_9 = Sketch_1.setDistance(SketchLine_13.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_12 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_13.result(), 175, type = "Direct")
+SketchLine_14 = Sketch_1.addLine(25.87956617560386, -26.24481087018098, -33.41116977659181, -17.04651590605781)
+SketchConstraintCoincidence_15 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_14.result())
+SketchConstraintDistance_10 = Sketch_1.setDistance(SketchLine_14.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_13 = Sketch_1.setAngle(SketchLine_13.result(), SketchLine_14.result(), 5, type = "Direct")
+SketchConstraintEqual_10 = Sketch_1.setEqual(SketchLine_13.result(), SketchLine_14.result())
+SketchLine_15 = Sketch_1.addLine(26.26360956157018, -22.77645725728195, -33.60319146957497, -18.78069271250733)
+SketchConstraintCoincidence_16 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_15.result())
+SketchConstraintEqual_11 = Sketch_1.setEqual(SketchLine_14.result(), SketchLine_15.result())
+SketchConstraintDistance_11 = Sketch_1.setDistance(SketchLine_15.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_14 = Sketch_1.setAngle(SketchLine_15.result(), SketchLine_14.result(), 5, type = "Direct")
+SketchConstraintCoincidence_17 = Sketch_1.setCoincident(SketchLine_7.startPoint(), SketchLine_15.result())
+
+SketchLine_16 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 78.00643494689578, -67.38706888260469)
+SketchLine_17 = Sketch_1.addLine(78.00643494689578, -67.38706888260469, 86.19687277706721, -49.141063069)
+SketchConstraintCoincidence_18 = Sketch_1.setCoincident(SketchLine_16.endPoint(), SketchLine_17.startPoint())
+SketchLine_18 = Sketch_1.addLine(86.19687277706721, -49.141063069, 69.83467154684908, 8.584826884311665)
+SketchConstraintCoincidence_19 = Sketch_1.setCoincident(SketchLine_17.endPoint(), SketchLine_18.startPoint())
+SketchLine_19 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_20 = Sketch_1.setCoincident(SketchLine_16.startPoint(), SketchLine_19.startPoint())
+SketchLine_20 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_21 = Sketch_1.setCoincident(SketchLine_19.endPoint(), SketchLine_20.endPoint())
+SketchLine_21 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_22 = Sketch_1.setCoincident(SketchLine_20.startPoint(), SketchLine_21.startPoint())
+SketchLine_22 = Sketch_1.addLine(47.45878924889096, -17.44298467461911, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_23 = Sketch_1.setCoincident(SketchLine_21.endPoint(), SketchLine_22.endPoint())
+SketchConstraintAngle_15 = Sketch_1.setAngle(SketchLine_16.result(), SketchLine_17.result(), 40, type = "Supplementary")
+SketchConstraintAngle_16 = Sketch_1.setAngle(SketchLine_18.result(), SketchLine_17.result(), 40, type = "Supplementary")
+SketchConstraintAngle_17 = Sketch_1.setAngle(SketchLine_16.result(), SketchLine_19.result(), 40, type = "Supplementary")
+SketchConstraintAngle_18 = Sketch_1.setAngle(SketchLine_21.result(), SketchLine_20.result(), 40, type = "Supplementary")
+SketchConstraintAngle_19 = Sketch_1.setAngle(SketchLine_20.result(), SketchLine_19.result(), 40, type = "Supplementary")
+SketchConstraintAngle_20 = Sketch_1.setAngle(SketchLine_21.result(), SketchLine_22.result(), 40, type = "Supplementary")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_17.result(), 20)
+SketchConstraintEqual_12 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_16.result())
+SketchConstraintEqual_13 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_19.result())
+SketchConstraintEqual_14 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_20.result())
+SketchConstraintEqual_15 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_21.result())
+SketchLine_23 = Sketch_1.addLine(92.44909133742235, -46.11522988707736, 57.3302344261388, 2.533160520466375)
+SketchPoint_3 = Sketch_1.addPoint(80.74280570032784, -29.89909975122945)
+SketchConstraintCoincidence_24 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_18.result())
+SketchConstraintCoincidence_25 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_23.result())
+SketchConstraintDistance_12 = Sketch_1.setDistance(SketchLine_17.endPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_13 = Sketch_1.setDistance(SketchLine_23.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_14 = Sketch_1.setDistance(SketchPoint_3.coordinates(), SketchLine_18.endPoint(), 40, True)
+SketchConstraintEqual_16 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_23.result())
+SketchLine_24 = Sketch_1.addLine(97.289359083568, -41.13349208617944, 47.64969893384747, -7.430315081329468)
+SketchConstraintCoincidence_26 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_24.result())
+SketchConstraintEqual_17 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_24.result())
+SketchConstraintDistance_15 = Sketch_1.setDistance(SketchLine_24.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintAngle_21 = Sketch_1.setAngle(SketchLine_18.result(), SketchLine_23.result(), 160, type = "Supplementary")
+SketchConstraintAngle_22 = Sketch_1.setAngle(SketchLine_24.result(), SketchLine_23.result(), 160, type = "Supplementary")
+SketchLine_25 = Sketch_1.addLine(81.65898287743471, -28.41422234660911, 29.27079119639352, 0.8343224443798635)
+SketchPoint_4 = Sketch_1.addPoint(64.19625231708766, -18.66470741627946)
+SketchConstraintCoincidence_27 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_25.result())
+SketchConstraintCoincidence_28 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_24.result())
+SketchConstraintDistance_16 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_25.startPoint(), 20, True)
+SketchConstraintDistance_17 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_24.endPoint(), 20, True)
+SketchConstraintEqual_18 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_25.result())
+SketchLine_26 = Sketch_1.addLine(95.51651619313817, -32.72404055044315, 45.95024650348294, -10.47426958610802)
+SketchConstraintCoincidence_29 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_26.result())
+SketchConstraintDistance_18 = Sketch_1.setDistance(SketchLine_26.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_23 = Sketch_1.setAngle(SketchLine_24.result(), SketchLine_25.result(), 5, type = "Supplementary")
+SketchConstraintAngle_24 = Sketch_1.setAngle(SketchLine_26.result(), SketchLine_25.result(), 5, type = "Supplementary")
+SketchLine_27 = Sketch_1.addLine(83.08667026246893, -25.23373396888348, 26.41541642632507, -5.526654311071399)
+SketchConstraintCoincidence_30 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_27.result())
+SketchConstraintEqual_19 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_27.result())
+SketchConstraintDistance_19 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_27.startPoint(), 20, True)
+SketchConstraintAngle_25 = Sketch_1.setAngle(SketchLine_27.result(), SketchLine_26.result(), 5, type = "Supplementary")
+SketchLine_28 = Sketch_1.addLine(102.9783774975822, -28.45994944924871, 44.80518972684035, -13.76708639979482)
+SketchConstraintCoincidence_31 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_28.result())
+SketchConstraintEqual_20 = Sketch_1.setEqual(SketchLine_27.result(), SketchLine_28.result())
+SketchConstraintDistance_20 = Sketch_1.setDistance(SketchLine_28.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_26 = Sketch_1.setAngle(SketchLine_27.result(), SketchLine_28.result(), 5, type = "Supplementary")
+SketchLine_29 = Sketch_1.addLine(103.6845113974033, -25.04259067061018, 44.45212277692985, -15.47576578911409)
+SketchConstraintCoincidence_32 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_29.result())
+SketchConstraintDistance_21 = Sketch_1.setDistance(SketchLine_29.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_27 = Sketch_1.setAngle(SketchLine_28.result(), SketchLine_29.result(), 175, type = "Supplementary")
+SketchConstraintEqual_21 = Sketch_1.setEqual(SketchLine_28.result(), SketchLine_29.result())
+SketchLine_30 = Sketch_1.addLine(104.0901158019603, -21.57669234933498, 44.24932057465131, -17.20871494975169)
+SketchConstraintCoincidence_33 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_30.result())
+SketchConstraintEqual_22 = Sketch_1.setEqual(SketchLine_29.result(), SketchLine_30.result())
+SketchConstraintDistance_22 = Sketch_1.setDistance(SketchLine_30.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_28 = Sketch_1.setAngle(SketchLine_30.result(), SketchLine_29.result(), 175, type = "Supplementary")
+SketchConstraintCoincidence_34 = Sketch_1.setCoincident(SketchLine_22.startPoint(), SketchLine_30.result())
+
+SketchLine_31 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 156.7284871354776, -69.4178124771163)
+SketchLine_32 = Sketch_1.addLine(156.7284871354776, -69.4178124771163, 164.8053019156854, -51.12122637461573)
+SketchConstraintCoincidence_35 = Sketch_1.setCoincident(SketchLine_31.endPoint(), SketchLine_32.startPoint())
+SketchLine_33 = Sketch_1.addLine(164.8053019156854, -51.12122637461573, 148.0844426183728, 6.501797360341752)
+SketchConstraintCoincidence_36 = Sketch_1.setCoincident(SketchLine_32.endPoint(), SketchLine_33.startPoint())
+SketchLine_34 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_37 = Sketch_1.setCoincident(SketchLine_31.startPoint(), SketchLine_34.startPoint())
+SketchLine_35 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_38 = Sketch_1.setCoincident(SketchLine_34.endPoint(), SketchLine_35.endPoint())
+SketchLine_36 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_39 = Sketch_1.setCoincident(SketchLine_35.startPoint(), SketchLine_36.startPoint())
+SketchLine_37 = Sketch_1.addLine(125.8708496231354, -19.66465768043951, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_40 = Sketch_1.setCoincident(SketchLine_36.endPoint(), SketchLine_37.endPoint())
+SketchConstraintAngle_29 = Sketch_1.setAngle(SketchLine_31.result(), SketchLine_32.result(), 220, type = "Backward")
+SketchConstraintAngle_30 = Sketch_1.setAngle(SketchLine_33.result(), SketchLine_32.result(), 220, type = "Backward")
+SketchConstraintAngle_31 = Sketch_1.setAngle(SketchLine_31.result(), SketchLine_34.result(), 220, type = "Backward")
+SketchConstraintAngle_32 = Sketch_1.setAngle(SketchLine_36.result(), SketchLine_35.result(), 220, type = "Backward")
+SketchConstraintAngle_33 = Sketch_1.setAngle(SketchLine_35.result(), SketchLine_34.result(), 220, type = "Backward")
+SketchConstraintAngle_34 = Sketch_1.setAngle(SketchLine_36.result(), SketchLine_37.result(), 220, type = "Backward")
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_32.result(), 20)
+SketchConstraintEqual_23 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_31.result())
+SketchConstraintEqual_24 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_34.result())
+SketchConstraintEqual_25 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_35.result())
+SketchConstraintEqual_26 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_36.result())
+SketchLine_38 = Sketch_1.addLine(171.0385831271055, -48.05657162886843, 135.6178801955327, 0.3724878688471688)
+SketchPoint_5 = Sketch_1.addPoint(159.2316821499145, -31.91355179629657)
+SketchConstraintCoincidence_41 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_33.result())
+SketchConstraintCoincidence_42 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_38.result())
+SketchConstraintDistance_23 = Sketch_1.setDistance(SketchLine_32.endPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_24 = Sketch_1.setDistance(SketchLine_38.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_25 = Sketch_1.setDistance(SketchPoint_5.coordinates(), SketchLine_33.endPoint(), 40, True)
+SketchConstraintEqual_27 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_38.result())
+SketchLine_39 = Sketch_1.addLine(175.8477778293761, -43.04483044571408, 125.9994907909914, -9.650994497461493)
+SketchConstraintCoincidence_43 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_39.result())
+SketchConstraintEqual_28 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_39.result())
+SketchConstraintDistance_26 = Sketch_1.setDistance(SketchLine_39.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintAngle_35 = Sketch_1.setAngle(SketchLine_33.result(), SketchLine_38.result(), 340, type = "Backward")
+SketchConstraintAngle_36 = Sketch_1.setAngle(SketchLine_39.result(), SketchLine_38.result(), 340, type = "Backward")
+SketchLine_40 = Sketch_1.addLine(160.1386077477389, -30.42300575990098, 107.5695439158812, -1.500807920835093)
+SketchPoint_6 = Sketch_1.addPoint(142.615586470453, -20.78227314687903)
+SketchConstraintCoincidence_44 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_40.result())
+SketchConstraintCoincidence_45 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_39.result())
+SketchConstraintDistance_27 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_40.startPoint(), 20, True)
+SketchConstraintDistance_28 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_39.endPoint(), 20, True)
+SketchConstraintEqual_29 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_40.result())
+SketchLine_41 = Sketch_1.addLine(147.1612701207084, -22.78891223719074, 124.3190003679524, -12.70545836667124)
+SketchConstraintCoincidence_46 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_41.result())
+SketchConstraintDistance_29 = Sketch_1.setDistance(SketchLine_41.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_37 = Sketch_1.setAngle(SketchLine_39.result(), SketchLine_40.result(), 185, type = "Backward")
+SketchConstraintAngle_38 = Sketch_1.setAngle(SketchLine_41.result(), SketchLine_40.result(), 355, type = "Backward")
+SketchLine_42 = Sketch_1.addLine(161.5464893301474, -27.23370065688114, 104.7537807510641, -7.879418126874779)
+SketchConstraintCoincidence_47 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_42.result())
+SketchConstraintEqual_30 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_42.result())
+SketchConstraintDistance_30 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_42.startPoint(), 20, True)
+SketchConstraintAngle_39 = Sketch_1.setAngle(SketchLine_42.result(), SketchLine_41.result(), 355, type = "Backward")
+SketchLine_43 = Sketch_1.addLine(181.4578745011218, -30.33615510881286, 123.1944424551186, -16.0053321659121)
+SketchConstraintCoincidence_48 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_43.result())
+SketchConstraintEqual_31 = Sketch_1.setEqual(SketchLine_42.result(), SketchLine_43.result())
+SketchConstraintDistance_31 = Sketch_1.setDistance(SketchLine_43.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_40 = Sketch_1.setAngle(SketchLine_42.result(), SketchLine_43.result(), 185, type = "Backward")
+SketchLine_44 = Sketch_1.addLine(182.1427435468735, -26.91447124022047, 122.8520079322427, -17.71617410020831)
+SketchConstraintCoincidence_49 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_44.result())
+SketchConstraintDistance_32 = Sketch_1.setDistance(SketchLine_44.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_41 = Sketch_1.setAngle(SketchLine_43.result(), SketchLine_44.result(), 355, type = "Backward")
+SketchConstraintEqual_32 = Sketch_1.setEqual(SketchLine_43.result(), SketchLine_44.result())
+SketchLine_45 = Sketch_1.addLine(182.5267870601237, -23.44611764141531, 122.6599861756176, -19.45035089961089)
+SketchConstraintCoincidence_50 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_45.result())
+SketchConstraintEqual_33 = Sketch_1.setEqual(SketchLine_44.result(), SketchLine_45.result())
+SketchConstraintDistance_33 = Sketch_1.setDistance(SketchLine_45.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_42 = Sketch_1.setAngle(SketchLine_45.result(), SketchLine_44.result(), 355, type = "Backward")
+SketchConstraintCoincidence_51 = Sketch_1.setCoincident(SketchLine_37.startPoint(), SketchLine_45.result())
+model.do()
+Extrusion_1_objects = [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_9f-SketchLine_15f-SketchLine_7f-SketchLine_6r-SketchLine_5f-SketchLine_4r"), model.selection("FACE", "Sketch_1/Face-SketchLine_16f-SketchLine_17f-SketchLine_18f-SketchLine_24f-SketchLine_30f-SketchLine_22f-SketchLine_21r-SketchLine_20f-SketchLine_19r"), model.selection("FACE", "Sketch_1/Face-SketchLine_31f-SketchLine_32f-SketchLine_33f-SketchLine_39f-SketchLine_45f-SketchLine_37f-SketchLine_36r-SketchLine_35f-SketchLine_34r")]
+Extrusion_1 = model.addExtrusion(Part_1_doc, Extrusion_1_objects, model.selection(), 10, 0)
+model.do()
+
+model.end()
+
+from GeomAPI import *
+
+REF_VOLUME = 25018.7130187615
+
+model.testNbResults(Extrusion_1, 3)
+model.testNbSubResults(Extrusion_1, [0, 0, 0])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.SOLID, [1, 1, 1])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.FACE, [11, 11, 11])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.EDGE, [54, 54, 54])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.VERTEX, [108, 108, 108])
+model.testResultsVolumes(Extrusion_1, [REF_VOLUME, REF_VOLUME, REF_VOLUME])
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintAngle_v20191210_2.py b/src/SketchPlugin/Test/TestConstraintAngle_v20191210_2.py
new file mode 100644 (file)
index 0000000..5abdc94
--- /dev/null
@@ -0,0 +1,262 @@
+# Copyright (C) 2014-2019  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+model.addParameter(Part_1_doc, "Ang", "140")
+model.addParameter(Part_1_doc, "dAng", "5")
+Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchLine_1 = Sketch_1.addLine(-17.48270627886662, -77.57247534383079, 0.4653113240225388, -68.74815303974528)
+SketchLine_2 = Sketch_1.addLine(0.4653113240225388, -68.74815303974528, 8.542125432770632, -50.45156664083665)
+SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+SketchLine_3 = Sketch_1.addLine(8.542125432770632, -50.45156664083665, -8.178735979228234, 7.171456480488104)
+SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+SketchLine_4 = Sketch_1.addLine(-17.48270627886662, -77.57247534383079, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
+SketchLine_5 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -36.90385046950819, -72.79553507559329)
+SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.endPoint())
+SketchLine_6 = Sketch_1.addLine(-48.71075203912585, -56.65251567631857, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.startPoint(), SketchLine_6.startPoint())
+SketchLine_7 = Sketch_1.addLine(-30.39232801419296, -18.99499937550162, -47.37883052420096, -36.69691533260352)
+SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_7.endPoint())
+SketchConstraintAngle_1 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_2.result(), "Ang", type = "Direct")
+SketchConstraintAngle_2 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_2.result(), "Ang", type = "Direct")
+SketchConstraintAngle_3 = Sketch_1.setAngle(SketchLine_1.result(), SketchLine_4.result(), "Ang", type = "Direct")
+SketchConstraintAngle_4 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_5.result(), "Ang", type = "Direct")
+SketchConstraintAngle_5 = Sketch_1.setAngle(SketchLine_5.result(), SketchLine_4.result(), "Ang", type = "Direct")
+SketchConstraintAngle_6 = Sketch_1.setAngle(SketchLine_6.result(), SketchLine_7.result(), "Ang", type = "Direct")
+SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_2.result(), 20)
+SketchConstraintEqual_1 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_1.result())
+SketchConstraintEqual_2 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_4.result())
+SketchConstraintEqual_3 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_5.result())
+SketchConstraintEqual_4 = Sketch_1.setEqual(SketchLine_2.result(), SketchLine_6.result())
+SketchLine_8 = Sketch_1.addLine(14.775406531722, -47.38691166633646, -20.64529817713098, 1.042146531487715)
+SketchPoint_1 = Sketch_1.addPoint(2.968504962104344, -31.24389226706174)
+SketchConstraintCoincidence_7 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_3.result())
+SketchConstraintCoincidence_8 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_8.result())
+SketchConstraintDistance_1 = Sketch_1.setDistance(SketchLine_2.endPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_2 = Sketch_1.setDistance(SketchLine_8.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintDistance_3 = Sketch_1.setDistance(SketchPoint_1.coordinates(), SketchLine_3.endPoint(), 40, True)
+SketchConstraintEqual_5 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_8.result())
+SketchLine_9 = Sketch_1.addLine(19.58460105006861, -42.37517030669127, -30.26368721382418, -8.981336187802668)
+SketchConstraintCoincidence_9 = Sketch_1.setCoincident(SketchPoint_1.coordinates(), SketchLine_9.result())
+SketchConstraintEqual_6 = Sketch_1.setEqual(SketchLine_3.result(), SketchLine_9.result())
+SketchConstraintDistance_4 = Sketch_1.setDistance(SketchLine_9.startPoint(), SketchPoint_1.coordinates(), 20, True)
+SketchConstraintAngle_7 = Sketch_1.setAngle(SketchLine_3.result(), SketchLine_8.result(), "(180-Ang)/2", type = "Direct")
+SketchConstraintAngle_8 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_8.result(), "(180-Ang)/2", type = "Direct")
+SketchLine_10 = Sketch_1.addLine(3.875430505227681, -29.75334619738324, -48.69363438803513, -0.8311502875301234)
+SketchPoint_2 = Sketch_1.addPoint(-13.64759112585992, -20.1126142274322)
+SketchConstraintCoincidence_10 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_10.result())
+SketchConstraintCoincidence_11 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_9.result())
+SketchConstraintDistance_5 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_10.startPoint(), 20, True)
+SketchConstraintDistance_6 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_9.endPoint(), 20, True)
+SketchConstraintEqual_7 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_10.result())
+SketchLine_11 = Sketch_1.addLine(0.001529676402264532, -26.13785899518266, -31.94417752476854, -12.03580011868412)
+SketchConstraintCoincidence_12 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_11.result())
+SketchConstraintDistance_7 = Sketch_1.setDistance(SketchLine_11.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_9 = Sketch_1.setAngle(SketchLine_9.result(), SketchLine_10.result(), "180-dAng", type = "Direct")
+SketchConstraintAngle_10 = Sketch_1.setAngle(SketchLine_11.result(), SketchLine_10.result(), "dAng", type = "Direct")
+SketchLine_12 = Sketch_1.addLine(5.283311970593094, -26.56404104269606, -51.50939731876596, -7.20976059690448)
+SketchConstraintCoincidence_13 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_12.result())
+SketchConstraintEqual_8 = Sketch_1.setEqual(SketchLine_9.result(), SketchLine_12.result())
+SketchConstraintDistance_8 = Sketch_1.setDistance(SketchPoint_2.coordinates(), SketchLine_12.startPoint(), 20, True)
+SketchConstraintAngle_11 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_11.result(), "dAng", type = "Direct")
+SketchLine_13 = Sketch_1.addLine(25.19469725542325, -29.66649476390714, -33.06873531650151, -15.33567395919474)
+SketchConstraintCoincidence_14 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_13.result())
+SketchConstraintEqual_9 = Sketch_1.setEqual(SketchLine_12.result(), SketchLine_13.result())
+SketchConstraintDistance_9 = Sketch_1.setDistance(SketchLine_13.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_12 = Sketch_1.setAngle(SketchLine_12.result(), SketchLine_13.result(), "180-dAng", type = "Direct")
+SketchLine_14 = Sketch_1.addLine(25.87956617560386, -26.24481087018098, -33.41116977659181, -17.04651590605781)
+SketchConstraintCoincidence_15 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_14.result())
+SketchConstraintDistance_10 = Sketch_1.setDistance(SketchLine_14.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_13 = Sketch_1.setAngle(SketchLine_13.result(), SketchLine_14.result(), "dAng", type = "Direct")
+SketchConstraintEqual_10 = Sketch_1.setEqual(SketchLine_13.result(), SketchLine_14.result())
+SketchLine_15 = Sketch_1.addLine(26.26360956157018, -22.77645725728195, -33.60319146957497, -18.78069271250733)
+SketchConstraintCoincidence_16 = Sketch_1.setCoincident(SketchPoint_2.coordinates(), SketchLine_15.result())
+SketchConstraintEqual_11 = Sketch_1.setEqual(SketchLine_14.result(), SketchLine_15.result())
+SketchConstraintDistance_11 = Sketch_1.setDistance(SketchLine_15.endPoint(), SketchPoint_2.coordinates(), 20, True)
+SketchConstraintAngle_14 = Sketch_1.setAngle(SketchLine_15.result(), SketchLine_14.result(), "dAng", type = "Direct")
+SketchConstraintCoincidence_17 = Sketch_1.setCoincident(SketchLine_7.startPoint(), SketchLine_15.result())
+SketchLine_16 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 78.00643494689578, -67.38706888260469)
+SketchLine_17 = Sketch_1.addLine(78.00643494689578, -67.38706888260469, 86.19687277706721, -49.141063069)
+SketchConstraintCoincidence_18 = Sketch_1.setCoincident(SketchLine_16.endPoint(), SketchLine_17.startPoint())
+SketchLine_18 = Sketch_1.addLine(86.19687277706721, -49.141063069, 69.83467154684908, 8.584826884311665)
+SketchConstraintCoincidence_19 = Sketch_1.setCoincident(SketchLine_17.endPoint(), SketchLine_18.startPoint())
+SketchLine_19 = Sketch_1.addLine(60.00388909712782, -76.09960829009104, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_20 = Sketch_1.setCoincident(SketchLine_16.startPoint(), SketchLine_19.startPoint())
+SketchLine_20 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 40.61282650688053, -71.20198727360641)
+SketchConstraintCoincidence_21 = Sketch_1.setCoincident(SketchLine_19.endPoint(), SketchLine_20.endPoint())
+SketchLine_21 = Sketch_1.addLine(28.90654086978602, -54.98585713775849, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_22 = Sketch_1.setCoincident(SketchLine_20.startPoint(), SketchLine_21.startPoint())
+SketchLine_22 = Sketch_1.addLine(47.45878924889096, -17.44298467461911, 30.36253333631381, -35.03892539532215)
+SketchConstraintCoincidence_23 = Sketch_1.setCoincident(SketchLine_21.endPoint(), SketchLine_22.endPoint())
+SketchConstraintAngle_15 = Sketch_1.setAngle(SketchLine_16.result(), SketchLine_17.result(), "180-Ang", type = "Supplementary")
+SketchConstraintAngle_16 = Sketch_1.setAngle(SketchLine_18.result(), SketchLine_17.result(), "180-Ang", type = "Supplementary")
+SketchConstraintAngle_17 = Sketch_1.setAngle(SketchLine_16.result(), SketchLine_19.result(), "180-Ang", type = "Supplementary")
+SketchConstraintAngle_18 = Sketch_1.setAngle(SketchLine_21.result(), SketchLine_20.result(), "180-Ang", type = "Supplementary")
+SketchConstraintAngle_19 = Sketch_1.setAngle(SketchLine_20.result(), SketchLine_19.result(), "180-Ang", type = "Supplementary")
+SketchConstraintAngle_20 = Sketch_1.setAngle(SketchLine_21.result(), SketchLine_22.result(), "180-Ang", type = "Supplementary")
+SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_17.result(), 20)
+SketchConstraintEqual_12 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_16.result())
+SketchConstraintEqual_13 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_19.result())
+SketchConstraintEqual_14 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_20.result())
+SketchConstraintEqual_15 = Sketch_1.setEqual(SketchLine_17.result(), SketchLine_21.result())
+SketchLine_23 = Sketch_1.addLine(92.44909133742235, -46.11522988707736, 57.3302344261388, 2.533160520466375)
+SketchPoint_3 = Sketch_1.addPoint(80.74280570032784, -29.89909975122945)
+SketchConstraintCoincidence_24 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_18.result())
+SketchConstraintCoincidence_25 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_23.result())
+SketchConstraintDistance_12 = Sketch_1.setDistance(SketchLine_17.endPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_13 = Sketch_1.setDistance(SketchLine_23.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintDistance_14 = Sketch_1.setDistance(SketchPoint_3.coordinates(), SketchLine_18.endPoint(), 40, True)
+SketchConstraintEqual_16 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_23.result())
+SketchLine_24 = Sketch_1.addLine(97.289359083568, -41.13349208617944, 47.64969893384747, -7.430315081329468)
+SketchConstraintCoincidence_26 = Sketch_1.setCoincident(SketchPoint_3.coordinates(), SketchLine_24.result())
+SketchConstraintEqual_17 = Sketch_1.setEqual(SketchLine_18.result(), SketchLine_24.result())
+SketchConstraintDistance_15 = Sketch_1.setDistance(SketchLine_24.startPoint(), SketchPoint_3.coordinates(), 20, True)
+SketchConstraintAngle_21 = Sketch_1.setAngle(SketchLine_18.result(), SketchLine_23.result(), "90+Ang/2", type = "Supplementary")
+SketchConstraintAngle_22 = Sketch_1.setAngle(SketchLine_24.result(), SketchLine_23.result(), "90+Ang/2", type = "Supplementary")
+SketchLine_25 = Sketch_1.addLine(81.65898287743471, -28.41422234660911, 29.27079119639352, 0.8343224443798635)
+SketchPoint_4 = Sketch_1.addPoint(64.19625231708766, -18.66470741627946)
+SketchConstraintCoincidence_27 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_25.result())
+SketchConstraintCoincidence_28 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_24.result())
+SketchConstraintDistance_16 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_25.startPoint(), 20, True)
+SketchConstraintDistance_17 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_24.endPoint(), 20, True)
+SketchConstraintEqual_18 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_25.result())
+SketchLine_26 = Sketch_1.addLine(95.51651619313817, -32.72404055044315, 45.95024650348294, -10.47426958610802)
+SketchConstraintCoincidence_29 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_26.result())
+SketchConstraintDistance_18 = Sketch_1.setDistance(SketchLine_26.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_23 = Sketch_1.setAngle(SketchLine_24.result(), SketchLine_25.result(), "dAng", type = "Supplementary")
+SketchConstraintAngle_24 = Sketch_1.setAngle(SketchLine_26.result(), SketchLine_25.result(), "dAng", type = "Supplementary")
+SketchLine_27 = Sketch_1.addLine(83.08667026246893, -25.23373396888348, 26.41541642632507, -5.526654311071399)
+SketchConstraintCoincidence_30 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_27.result())
+SketchConstraintEqual_19 = Sketch_1.setEqual(SketchLine_24.result(), SketchLine_27.result())
+SketchConstraintDistance_19 = Sketch_1.setDistance(SketchPoint_4.coordinates(), SketchLine_27.startPoint(), 20, True)
+SketchConstraintAngle_25 = Sketch_1.setAngle(SketchLine_27.result(), SketchLine_26.result(), "dAng", type = "Supplementary")
+SketchLine_28 = Sketch_1.addLine(102.9783774975822, -28.45994944924871, 44.80518972684035, -13.76708639979482)
+SketchConstraintCoincidence_31 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_28.result())
+SketchConstraintEqual_20 = Sketch_1.setEqual(SketchLine_27.result(), SketchLine_28.result())
+SketchConstraintDistance_20 = Sketch_1.setDistance(SketchLine_28.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_26 = Sketch_1.setAngle(SketchLine_27.result(), SketchLine_28.result(), "dAng", type = "Supplementary")
+SketchLine_29 = Sketch_1.addLine(103.6845113974033, -25.04259067061018, 44.45212277692985, -15.47576578911409)
+SketchConstraintCoincidence_32 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_29.result())
+SketchConstraintDistance_21 = Sketch_1.setDistance(SketchLine_29.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_27 = Sketch_1.setAngle(SketchLine_28.result(), SketchLine_29.result(), "180-dAng", type = "Supplementary")
+SketchConstraintEqual_21 = Sketch_1.setEqual(SketchLine_28.result(), SketchLine_29.result())
+SketchLine_30 = Sketch_1.addLine(104.0901158019603, -21.57669234933498, 44.24932057465131, -17.20871494975169)
+SketchConstraintCoincidence_33 = Sketch_1.setCoincident(SketchPoint_4.coordinates(), SketchLine_30.result())
+SketchConstraintEqual_22 = Sketch_1.setEqual(SketchLine_29.result(), SketchLine_30.result())
+SketchConstraintDistance_22 = Sketch_1.setDistance(SketchLine_30.endPoint(), SketchPoint_4.coordinates(), 20, True)
+SketchConstraintAngle_28 = Sketch_1.setAngle(SketchLine_30.result(), SketchLine_29.result(), "180-dAng", type = "Supplementary")
+SketchConstraintCoincidence_34 = Sketch_1.setCoincident(SketchLine_22.startPoint(), SketchLine_30.result())
+
+SketchLine_31 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 156.7284871354776, -69.4178124771163)
+SketchLine_32 = Sketch_1.addLine(156.7284871354776, -69.4178124771163, 164.8053019156854, -51.12122637461573)
+SketchConstraintCoincidence_35 = Sketch_1.setCoincident(SketchLine_31.endPoint(), SketchLine_32.startPoint())
+SketchLine_33 = Sketch_1.addLine(164.8053019156854, -51.12122637461573, 148.0844426183728, 6.501797360341752)
+SketchConstraintCoincidence_36 = Sketch_1.setCoincident(SketchLine_32.endPoint(), SketchLine_33.startPoint())
+SketchLine_34 = Sketch_1.addLine(138.7804692087479, -78.2421341225341, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_37 = Sketch_1.setCoincident(SketchLine_31.startPoint(), SketchLine_34.startPoint())
+SketchLine_35 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 119.3593251934135, -73.46519314156717)
+SketchConstraintCoincidence_38 = Sketch_1.setCoincident(SketchLine_34.endPoint(), SketchLine_35.endPoint())
+SketchLine_36 = Sketch_1.addLine(107.5524242162226, -57.32217330899532, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_39 = Sketch_1.setCoincident(SketchLine_35.startPoint(), SketchLine_36.startPoint())
+SketchLine_37 = Sketch_1.addLine(125.8708496231354, -19.66465768043951, 108.8843464634908, -37.36657301415998)
+SketchConstraintCoincidence_40 = Sketch_1.setCoincident(SketchLine_36.endPoint(), SketchLine_37.endPoint())
+SketchConstraintAngle_29 = Sketch_1.setAngle(SketchLine_31.result(), SketchLine_32.result(), "360-Ang", type = "Backward")
+SketchConstraintAngle_30 = Sketch_1.setAngle(SketchLine_33.result(), SketchLine_32.result(), "360-Ang", type = "Backward")
+SketchConstraintAngle_31 = Sketch_1.setAngle(SketchLine_31.result(), SketchLine_34.result(), "360-Ang", type = "Backward")
+SketchConstraintAngle_32 = Sketch_1.setAngle(SketchLine_36.result(), SketchLine_35.result(), "360-Ang", type = "Backward")
+SketchConstraintAngle_33 = Sketch_1.setAngle(SketchLine_35.result(), SketchLine_34.result(), "360-Ang", type = "Backward")
+SketchConstraintAngle_34 = Sketch_1.setAngle(SketchLine_36.result(), SketchLine_37.result(), "360-Ang", type = "Backward")
+SketchConstraintLength_3 = Sketch_1.setLength(SketchLine_32.result(), 20)
+SketchConstraintEqual_23 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_31.result())
+SketchConstraintEqual_24 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_34.result())
+SketchConstraintEqual_25 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_35.result())
+SketchConstraintEqual_26 = Sketch_1.setEqual(SketchLine_32.result(), SketchLine_36.result())
+SketchLine_38 = Sketch_1.addLine(171.0385831271055, -48.05657162886843, 135.6178801955327, 0.3724878688471688)
+SketchPoint_5 = Sketch_1.addPoint(159.2316821499145, -31.91355179629657)
+SketchConstraintCoincidence_41 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_33.result())
+SketchConstraintCoincidence_42 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_38.result())
+SketchConstraintDistance_23 = Sketch_1.setDistance(SketchLine_32.endPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_24 = Sketch_1.setDistance(SketchLine_38.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintDistance_25 = Sketch_1.setDistance(SketchPoint_5.coordinates(), SketchLine_33.endPoint(), 40, True)
+SketchConstraintEqual_27 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_38.result())
+SketchLine_39 = Sketch_1.addLine(175.8477778293761, -43.04483044571408, 125.9994907909914, -9.650994497461493)
+SketchConstraintCoincidence_43 = Sketch_1.setCoincident(SketchPoint_5.coordinates(), SketchLine_39.result())
+SketchConstraintEqual_28 = Sketch_1.setEqual(SketchLine_33.result(), SketchLine_39.result())
+SketchConstraintDistance_26 = Sketch_1.setDistance(SketchLine_39.startPoint(), SketchPoint_5.coordinates(), 20, True)
+SketchConstraintAngle_35 = Sketch_1.setAngle(SketchLine_33.result(), SketchLine_38.result(), "360-(180-Ang)/2", type = "Backward")
+SketchConstraintAngle_36 = Sketch_1.setAngle(SketchLine_39.result(), SketchLine_38.result(), "360-(180-Ang)/2", type = "Backward")
+SketchLine_40 = Sketch_1.addLine(160.1386077477389, -30.42300575990098, 107.5695439158812, -1.500807920835093)
+SketchPoint_6 = Sketch_1.addPoint(142.615586470453, -20.78227314687903)
+SketchConstraintCoincidence_44 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_40.result())
+SketchConstraintCoincidence_45 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_39.result())
+SketchConstraintDistance_27 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_40.startPoint(), 20, True)
+SketchConstraintDistance_28 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_39.endPoint(), 20, True)
+SketchConstraintEqual_29 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_40.result())
+SketchLine_41 = Sketch_1.addLine(147.1612701207084, -22.78891223719074, 124.3190003679524, -12.70545836667124)
+SketchConstraintCoincidence_46 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_41.result())
+SketchConstraintDistance_29 = Sketch_1.setDistance(SketchLine_41.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_37 = Sketch_1.setAngle(SketchLine_39.result(), SketchLine_40.result(), "180+dAng", type = "Backward")
+SketchConstraintAngle_38 = Sketch_1.setAngle(SketchLine_41.result(), SketchLine_40.result(), "360-dAng", type = "Backward")
+SketchLine_42 = Sketch_1.addLine(161.5464893301474, -27.23370065688114, 104.7537807510641, -7.879418126874779)
+SketchConstraintCoincidence_47 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_42.result())
+SketchConstraintEqual_30 = Sketch_1.setEqual(SketchLine_39.result(), SketchLine_42.result())
+SketchConstraintDistance_30 = Sketch_1.setDistance(SketchPoint_6.coordinates(), SketchLine_42.startPoint(), 20, True)
+SketchConstraintAngle_39 = Sketch_1.setAngle(SketchLine_42.result(), SketchLine_41.result(), "360-dAng", type = "Backward")
+SketchLine_43 = Sketch_1.addLine(181.4578745011218, -30.33615510881286, 123.1944424551186, -16.0053321659121)
+SketchConstraintCoincidence_48 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_43.result())
+SketchConstraintEqual_31 = Sketch_1.setEqual(SketchLine_42.result(), SketchLine_43.result())
+SketchConstraintDistance_31 = Sketch_1.setDistance(SketchLine_43.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_40 = Sketch_1.setAngle(SketchLine_42.result(), SketchLine_43.result(), "180+dAng", type = "Backward")
+SketchLine_44 = Sketch_1.addLine(182.1427435468735, -26.91447124022047, 122.8520079322427, -17.71617410020831)
+SketchConstraintCoincidence_49 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_44.result())
+SketchConstraintDistance_32 = Sketch_1.setDistance(SketchLine_44.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_41 = Sketch_1.setAngle(SketchLine_43.result(), SketchLine_44.result(), "360-dAng", type = "Backward")
+SketchConstraintEqual_32 = Sketch_1.setEqual(SketchLine_43.result(), SketchLine_44.result())
+SketchLine_45 = Sketch_1.addLine(182.5267870601237, -23.44611764141531, 122.6599861756176, -19.45035089961089)
+SketchConstraintCoincidence_50 = Sketch_1.setCoincident(SketchPoint_6.coordinates(), SketchLine_45.result())
+SketchConstraintEqual_33 = Sketch_1.setEqual(SketchLine_44.result(), SketchLine_45.result())
+SketchConstraintDistance_33 = Sketch_1.setDistance(SketchLine_45.endPoint(), SketchPoint_6.coordinates(), 20, True)
+SketchConstraintAngle_42 = Sketch_1.setAngle(SketchLine_45.result(), SketchLine_44.result(), "360-dAng", type = "Backward")
+SketchConstraintCoincidence_51 = Sketch_1.setCoincident(SketchLine_37.startPoint(), SketchLine_45.result())
+model.do()
+Extrusion_1_objects = [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2f-SketchLine_3f-SketchLine_9f-SketchLine_15f-SketchLine_7f-SketchLine_6r-SketchLine_5f-SketchLine_4r"), model.selection("FACE", "Sketch_1/Face-SketchLine_16f-SketchLine_17f-SketchLine_18f-SketchLine_24f-SketchLine_30f-SketchLine_22f-SketchLine_21r-SketchLine_20f-SketchLine_19r"), model.selection("FACE", "Sketch_1/Face-SketchLine_31f-SketchLine_32f-SketchLine_33f-SketchLine_39f-SketchLine_45f-SketchLine_37f-SketchLine_36r-SketchLine_35f-SketchLine_34r")]
+Extrusion_1 = model.addExtrusion(Part_1_doc, Extrusion_1_objects, model.selection(), 10, 0)
+model.do()
+
+model.end()
+
+from GeomAPI import *
+
+REF_VOLUME = 25018.7130187615
+
+model.testNbResults(Extrusion_1, 3)
+model.testNbSubResults(Extrusion_1, [0, 0, 0])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.SOLID, [1, 1, 1])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.FACE, [11, 11, 11])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.EDGE, [54, 54, 54])
+model.testNbSubShapes(Extrusion_1, GeomAPI_Shape.VERTEX, [108, 108, 108])
+model.testResultsVolumes(Extrusion_1, [REF_VOLUME, REF_VOLUME, REF_VOLUME])
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestConstraintCoincidenceBSpline.py b/src/SketchPlugin/Test/TestConstraintCoincidenceBSpline.py
new file mode 100644 (file)
index 0000000..36a0426
--- /dev/null
@@ -0,0 +1,176 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test constraint coincidence applied for B-spline curve and its sub-results
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+
+from GeomAPI import *
+from SketchAPI import *
+
+__updated__ = "2020-01-21"
+
+class TestCoincidenceBSpline(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+
+    self.myPoles = [GeomAPI_Pnt2d(-10, -30), GeomAPI_Pnt2d(20, -15), GeomAPI_Pnt2d(-10, 0), GeomAPI_Pnt2d(20, 15), GeomAPI_Pnt2d(-10, 30)]
+    self.myWeights = [1, 3, 5, 3, 1]
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = self.myWeights)
+    self.myControlPoles = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    self.myControlLines = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3])
+
+    self.myDOF = len(self.myPoles) * 2
+    self.myOrigin = self.mySketch.addPoint("Origin")
+    self.myOX = self.mySketch.addLine("OX")
+    model.do()
+    self.myExpectFailure = False
+    self.myNbPoints = len(self.myPoles) + 1
+    self.myNbLines = len(self.myPoles)
+    self.myNbBSplines = 1
+    self.myNbInternalConstraints = len(self.myPoles) * 3 - 2
+    self.myNbCoincidences = 1
+
+  def tearDown(self):
+    model.end()
+    if self.myExpectFailure:
+      assert(self.mySketch.solverError() != ""), "PlaneGCS limitation: if you see this message, then PlaneGCS has solved the set of constraints correctly"
+      model.undo()
+    else:
+      self.checkDOF()
+      model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints)
+      model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines)
+      model.testNbSubFeatures(self.mySketch, "SketchBSpline", self.myNbBSplines)
+      model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", self.myNbInternalConstraints)
+      model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", self.myNbCoincidences)
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def setCoincidentWithOrigin(self, thePoint):
+    self.mySketch.setCoincident(thePoint, self.myOrigin.coordinates())
+    self.myDOF -= 2
+    model.do()
+
+  def setCoincidentWithOX(self, thePoint):
+    self.mySketch.setCoincident(thePoint, self.myOX.result())
+    self.myDOF -= 1
+    model.do()
+
+  def assertPoles(self):
+    poles = self.mySpline.poles()
+    assert(poles.size() == len(self.myPoles))
+    for index in range(0, len(self.myPoles)):
+      self.assertPoints(self.myPoles[index], poles.pnt(index))
+
+  def assertPoints(self, thePoint1, thePoint2):
+    self.assertAlmostEqual(thePoint1.x(), thePoint2.x())
+    self.assertAlmostEqual(thePoint1.y(), thePoint2.y())
+
+  def assertPointOnLine(self, thePoint, theLineStart, theLineEnd):
+    vecP = [thePoint.x() - theLineStart.x(), thePoint.y() - theLineStart.y()]
+    vecL = [theLineEnd.x() - theLineStart.x(), theLineEnd.y() - theLineStart.y()]
+    dist = math.fabs(vecP[0] * vecL[1] - vecP[1] * vecL[0]) / math.hypot(vecL[0], vecL[1])
+    self.assertAlmostEqual(dist, 0.0)
+
+  def assertPointOnSpline(self, thePoint, theSpline):
+    point = GeomAPI_Pnt(thePoint.x(), thePoint.y(), 0.0)
+    bspline = GeomAPI_Curve(theSpline.results()[-1].resultSubShapePair()[0].shape())
+    proj = bspline.project(point)
+    self.assertAlmostEqual(point.distance(proj), 0.0)
+
+
+  def test_origin_equal_start_point(self):
+    """ Test 1. Make start point of B-spline coincident with the Origin
+    """
+    self.setCoincidentWithOrigin(self.mySpline.startPoint())
+    self.myPoles[0].setX(0)
+    self.myPoles[0].setY(0)
+    self.assertPoles()
+
+  def test_origin_equal_end_point(self):
+    """ Test 2. Make end point of B-spline coincident with the Origin
+    """
+    self.setCoincidentWithOrigin(self.mySpline.endPoint())
+    self.myPoles[-1].setX(0)
+    self.myPoles[-1].setY(0)
+    self.assertPoles()
+
+  def test_origin_equal_pole(self):
+    """ Test 3. Make one of B-spline poles coincident with the Origin
+    """
+    self.setCoincidentWithOrigin(SketchAPI_Point(self.myControlPoles[1]).coordinates())
+    self.myPoles[1].setX(0)
+    self.myPoles[1].setY(0)
+    self.assertPoles()
+
+  def test_origin_on_bspline(self):
+    """ Test 4. (expected failure) Make Origin lying on the B-spline curve
+    """
+    self.mySketch.setCoincident(self.mySpline.defaultResult(), self.myOrigin.coordinates())
+    self.myDOF -= 1
+    model.do()
+    self.myExpectFailure = True
+
+  def test_point_on_bspline(self):
+    """ Test 5. Place free point on the B-spline curve
+    """
+    point = self.mySketch.addPoint(1, 0)
+    self.mySketch.setCoincident(self.myOX.defaultResult(), point.coordinates())
+    self.mySketch.setCoincident(self.mySpline.defaultResult(), point.coordinates())
+    model.do()
+    self.myNbPoints += 1
+    self.myNbCoincidences += 1
+    self.assertPointOnSpline(point.coordinates(), self.mySpline)
+
+
+  def test_start_point_on_axis(self):
+    """ Test 6. Make start point of B-spline coincident with the OX
+    """
+    self.setCoincidentWithOX(self.mySpline.startPoint())
+    self.myPoles[0].setY(0)
+    self.assertPoles()
+
+  def test_end_point_on_axis(self):
+    """ Test 7. Make end point of B-spline coincident with the OX
+    """
+    self.setCoincidentWithOX(self.mySpline.endPoint())
+    self.myPoles[-1].setY(0)
+    self.assertPoles()
+
+  def test_pole_on_axis(self):
+    """ Test 8. Make one of B-spline poles coincident with the OX
+    """
+    self.setCoincidentWithOX(SketchAPI_Point(self.myControlPoles[1]).coordinates())
+    self.myPoles[1].setY(0)
+    self.assertPoles()
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
index e9f4d35d4a70eae62d2d07df7b0a05eb7f582755..dc741d409691edcdd0ece73c532aed4098ca2cf7 100644 (file)
@@ -66,12 +66,12 @@ class TestMiddlePointOnEllipticArc(unittest.TestCase):
     self.checkPointOnEllipse(thePoint, anEllipse)
     # check angles
     TOLERANCE = 1.e-5
-    startAngle = 0; startPoint = GeomAPI_Pnt(theArc.startPoint().x(), theArc.startPoint().y(), 0)
-    startAngle = anEllipse.parameter(startPoint, TOLERANCE, startAngle)
-    endAngle = 0; endPoint = GeomAPI_Pnt(theArc.endPoint().x(), theArc.endPoint().y(), 0)
-    endAngle = anEllipse.parameter(endPoint, TOLERANCE, endAngle)
-    midAngle = 0; midPoint = GeomAPI_Pnt(thePoint.x(), thePoint.y(), 0)
-    midAngle = anEllipse.parameter(midPoint, TOLERANCE, midAngle)
+    startPoint = GeomAPI_Pnt(theArc.startPoint().x(), theArc.startPoint().y(), 0)
+    isCalculated, startAngle = anEllipse.parameter(startPoint, TOLERANCE)
+    endPoint = GeomAPI_Pnt(theArc.endPoint().x(), theArc.endPoint().y(), 0)
+    isCalculated, endAngle = anEllipse.parameter(endPoint, TOLERANCE)
+    midPoint = GeomAPI_Pnt(thePoint.x(), thePoint.y(), 0)
+    isCalculated, midAngle = anEllipse.parameter(midPoint, TOLERANCE)
     diffMS = self.toPeriod(midAngle - startAngle)
     diffEM = self.toPeriod(endAngle - midAngle)
     self.assertAlmostEqual(diffMS, diffEM)
diff --git a/src/SketchPlugin/Test/TestConstraintTangentBSpline.py b/src/SketchPlugin/Test/TestConstraintTangentBSpline.py
new file mode 100644 (file)
index 0000000..e3e758b
--- /dev/null
@@ -0,0 +1,479 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test constraint "Tangent" applied to B-spline and another entity
+"""
+
+import unittest
+import math
+
+from salome.shaper import model
+
+from GeomAPI import *
+from GeomAlgoAPI import *
+from SketchAPI import *
+
+__updated__ = "2020-01-22"
+
+class TestTangentBSpline(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myPoles = [GeomAPI_Pnt2d(-10, -30), GeomAPI_Pnt2d(20, -15), GeomAPI_Pnt2d(-10, 0), GeomAPI_Pnt2d(20, 15), GeomAPI_Pnt2d(-10, 30)]
+    self.myWeights = [1, 3, 5, 3, 1]
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = self.myWeights)
+    self.myControlPoles = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    self.myControlLines = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3])
+    model.do()
+
+    self.myExpectedFailure = False
+    self.myDOF = len(self.myPoles) * 2
+    self.myNbPoints = len(self.myPoles)
+    self.myNbLines = len(self.myPoles) - 1
+    self.myNbArcs = 0
+    self.myNbCircles = 0
+    self.myNbEllipses = 0
+    self.myNbEllipticArcs = 0
+    self.myNbBSplines = 1
+    self.myNbInternals = len(self.myPoles) * 3 - 2
+    self.myNbCoincidence = 0
+    self.myNbTangency = 0
+
+  def tearDown(self):
+    model.end()
+    if self.myExpectedFailure:
+      assert(self.mySketch.solverError() != ""), "PlaneGCS limitation: if you see this message, then PlaneGCS has solved the set of constraints correctly"
+      model.undo()
+    else:
+      self.checkDOF()
+      model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints)
+      model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines)
+      model.testNbSubFeatures(self.mySketch, "SketchArc", self.myNbArcs)
+      model.testNbSubFeatures(self.mySketch, "SketchCircle", self.myNbCircles)
+      model.testNbSubFeatures(self.mySketch, "SketchEllipse", self.myNbEllipses)
+      model.testNbSubFeatures(self.mySketch, "SketchEllipticArc", self.myNbEllipticArcs)
+      model.testNbSubFeatures(self.mySketch, "SketchBSpline", self.myNbBSplines)
+      model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", self.myNbInternals)
+      model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", self.myNbCoincidence)
+      model.testNbSubFeatures(self.mySketch, "SketchConstraintTangent", self.myNbTangency)
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def assertTangentFeatures(self, theFeature1, theFeature2):
+    shapes = [theFeature1.results()[-1].resultSubShapePair()[0].shape(),
+              theFeature2.results()[-1].resultSubShapePair()[0].shape()]
+    for s in shapes:
+      e = shapeToEdge(s)
+      if e.isLine() or e.isArc() or e.isEllipse():
+        params = e.getRange()
+        e.setRange(params[0] - 100, params[1] + 100)
+    # TODO (azv): complete checking the tangent curves
+
+  def assertPointLineDistance(self, thePoint, theLine, theExpectedDistance = 0):
+    dist = model.distancePointLine(thePoint, theLine)
+    self.assertAlmostEqual(dist, theExpectedDistance)
+
+  def assertTangentLineCircle(self, theLine, theCircle):
+    self.assertPointLineDistance(theCircle.center(), theLine, theCircle.radius().value())
+
+  def assertTangentLineEllipse(self, theLine, theEllipse):
+    aLine = GeomAPI_Lin2d(theLine.startPoint().pnt(), theLine.endPoint().pnt())
+    projF1 = aLine.project(theEllipse.firstFocus().pnt())
+    projF2 = aLine.project(theEllipse.secondFocus().pnt())
+
+    distF1P1 = model.distancePointPoint(theEllipse.firstFocus(), projF1)
+    distF2P2 = model.distancePointPoint(theEllipse.secondFocus(), projF2)
+
+    tgPoint = GeomAPI_Pnt2d((projF1.x() * distF2P2 + projF2.x() * distF1P1) / (distF1P1 + distF2P2), (projF1.y() * distF2P2 + projF2.y() * distF1P1) / (distF1P1 + distF2P2))
+    distF1T = model.distancePointPoint(theEllipse.firstFocus(), tgPoint)
+    distF2T = model.distancePointPoint(theEllipse.secondFocus(), tgPoint)
+    self.assertAlmostEqual(distF1T + distF2T, 2 * theEllipse.majorRadius().value())
+
+
+  def test_line_tangent(self):
+    """ Test 1. Set tangency between B-spline and a line
+    """
+    aLine = self.mySketch.addLine(10, -10, 90, 40)
+    self.myNbLines += 1
+    self.myDOF += 4
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aLine.result())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentFeatures(aLine, self.mySpline)
+
+  def test_circle_tangent(self):
+    """ Test 2. Set tangency between B-spline and a circle
+    """
+    aCircle = self.mySketch.addCircle(10, 10, 20)
+    self.myNbCircles += 1
+    self.myDOF += 3
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aCircle.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentFeatures(aCircle, self.mySpline)
+
+  def test_arc_tangent(self):
+    """ Test 3. Set tangency between B-spline and an arc
+    """
+    anArc = self.mySketch.addArc(10, 10, 20, 10, 10, 20, False)
+    self.myNbArcs += 1
+    self.myDOF += 5
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentFeatures(anArc, self.mySpline)
+
+  def test_ellipse_tangent(self):
+    """ Test 4. Set tangency between B-spline and an ellipse
+    """
+    anEllipse = self.mySketch.addEllipse(10, 10, 20, 10, 7)
+    self.myNbEllipses += 1
+    self.myDOF += 5
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anEllipse.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentFeatures(anEllipse, self.mySpline)
+
+  def test_elliptic_arc_tangent(self):
+    """ Test 5. Set tangency between B-spline and an elliptic arc
+    """
+    anEllipticArc = self.mySketch.addEllipticArc(10, 10, 20, 10, 22.2065556157337, 10, 10, 17, True)
+    self.myNbEllipticArcs += 1
+    self.myDOF += 7
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentFeatures(anEllipticArc, self.mySpline)
+
+  def test_spline_tangent(self):
+    """ Test 6. Set tangency between two B-spline curves
+    """
+    aSpline = self.mySketch.addSpline(poles = [(50, -20), (40, 0), (50, 20)])
+    self.myNbBSplines += 1
+    self.myDOF += aSpline.poles().size() * 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aSpline.result())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    #self.assertTangentFeatures(aSpline, self.mySpline)
+    self.myExpectedFailure = True
+
+
+  def test_line_tangent_coincident_by_pole(self):
+    """ Test 7. Set tangency between B-spline and a line coincident with B-spline start point
+    """
+    aLine = self.mySketch.addLine(-15, -25, 50, 40)
+    self.myNbLines += 1
+    self.myDOF += 4
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), aLine.result())
+    self.myNbCoincidence += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aLine.result())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertPointLineDistance(self.mySpline.startPoint(), aLine)
+    self.assertTangentFeatures(aLine, self.mySpline)
+
+  def test_circle_tangent_coincident_by_pole(self):
+    """ Test 8. Set tangency between B-spline and a circle coincident with B-spline end point
+    """
+    aCircle = self.mySketch.addCircle(10, 10, 20)
+    self.myNbCircles += 1
+    self.myDOF += 3
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), aCircle.defaultResult())
+    self.myNbCoincidence += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aCircle.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentFeatures(aCircle, self.mySpline)
+    dist = model.distancePointPoint(self.mySpline.startPoint(), aCircle.center())
+    self.assertAlmostEqual(dist, aCircle.radius().value())
+
+  def test_arc_tangent_coincident_by_pole(self):
+    """ Test 9. Set tangency between B-spline and an arc coincident with B-spline end point
+    """
+    anArc = self.mySketch.addArc(10, 10, 20, 10, 10, 20, False)
+    self.myNbArcs += 1
+    self.myDOF += 5
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.endPoint(), anArc.defaultResult())
+    self.myNbCoincidence += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentFeatures(anArc, self.mySpline)
+    dist = model.distancePointPoint(self.mySpline.endPoint(), anArc.center())
+    self.assertAlmostEqual(dist, anArc.radius().value())
+
+  def test_ellipse_tangent_coincident_by_pole(self):
+    """ Test 10. Set tangency between B-spline and an ellipse coincident with B-spline start point
+    """
+    anEllipse = self.mySketch.addEllipse(10, 10, 20, 10, 7)
+    self.myNbEllipses += 1
+    self.myDOF += 5
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), anEllipse.defaultResult())
+    self.myNbCoincidence += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anEllipse.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentLineEllipse(SketchAPI_Line(self.myControlLines[0]), anEllipse)
+
+  def test_elliptic_arc_tangent_coincident_by_pole(self):
+    """ Test 11. Set tangency between B-spline and an elliptic arc coincident with B-spline start point
+    """
+    anEllipticArc = self.mySketch.addEllipticArc(10, 10, 20, 10, 22.2065556157337, 10, 10, 17, True)
+    self.myNbEllipticArcs += 1
+    self.myDOF += 7
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), anEllipticArc.defaultResult())
+    self.myNbCoincidence += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentLineEllipse(SketchAPI_Line(self.myControlLines[0]), anEllipticArc)
+
+
+  def test_line_tangent_coincident_by_boundaries(self):
+    """ Test 12. Set tangency between B-spline and a line, coincident by their start points
+    """
+    aLine = self.mySketch.addLine(10, -10, 90, 40)
+    self.myNbLines += 1
+    self.myDOF += 4
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), aLine.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aLine.result())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertPointLineDistance(aLine.endPoint(), self.myControlLines[0])
+
+  def test_arc_tangent_coincident_by_boundaries(self):
+    """ Test 13. Set tangency between B-spline and an arc, coincident by their start points
+    """
+    anArc = self.mySketch.addArc(10, 10, 20, 10, 10, 20, False)
+    self.myNbArcs += 1
+    self.myDOF += 5
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), anArc.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentLineCircle(SketchAPI_Line(self.myControlLines[0]), anArc)
+
+  def test_elliptic_arc_tangent_coincident_by_boundaries(self):
+    """ Test 14. Set tangency between B-spline and an elliptic arc, coincident by their start points
+    """
+    anEllipticArc = self.mySketch.addEllipticArc(10, -10, 20, -10, 22.2065556157337, -10, 10, 3, True)
+    self.myNbEllipticArcs += 1
+    self.myDOF += 7
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), anEllipticArc.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentLineEllipse(SketchAPI_Line(self.myControlLines[0]), anEllipticArc)
+
+  def test_spline_tangent_coincident_by_boundaries(self):
+    """ Test 15. Set tangency between two B-spline curves coincident with B-spline start point
+    """
+    aSpline = self.mySketch.addSpline(poles = [(50, -20), (40, 0), (50, 20)])
+    self.myNbBSplines += 1
+    self.myDOF += aSpline.poles().size() * 2
+    model.do()
+
+    self.mySketch.setCoincident(self.mySpline.startPoint(), aSpline.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aSpline.result())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    #self.assertPointLineDistance(aSpline.poles()[1], self.myControlLines[0])
+    self.myExpectedFailure = True
+
+
+  def test_line_tangent_coincident_by_aux(self):
+    """ Test 16. Set tangency between B-spline and a line, coincident by their start points
+    """
+    aLine = self.mySketch.addLine(10, -10, 90, 40)
+    self.myNbLines += 1
+    self.myDOF += 4
+    model.do()
+
+    self.mySketch.setCoincident(SketchAPI_Point(self.myControlPoles[0]).coordinates(), aLine.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aLine.result())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertPointLineDistance(aLine.endPoint(), self.myControlLines[0])
+
+  def test_arc_tangent_coincident_by_aux(self):
+    """ Test 17. Set tangency between B-spline and an arc, coincident by their start points
+    """
+    anArc = self.mySketch.addArc(10, 10, 20, 10, 10, 20, False)
+    self.myNbArcs += 1
+    self.myDOF += 5
+    model.do()
+
+    self.mySketch.setCoincident(SketchAPI_Point(self.myControlPoles[0]).coordinates(), anArc.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentLineCircle(SketchAPI_Line(self.myControlLines[0]), anArc)
+
+  def test_elliptic_arc_tangent_coincident_by_aux(self):
+    """ Test 18. Set tangency between B-spline and an elliptic arc, coincident by their start points
+    """
+    anEllipticArc = self.mySketch.addEllipticArc(10, 10, 20, 10, 22.2065556157337, 10, 10, 17, True)
+    self.myNbEllipticArcs += 1
+    self.myDOF += 7
+    model.do()
+
+    self.mySketch.setCoincident(SketchAPI_Point(self.myControlPoles[0]).coordinates(), anEllipticArc.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), anEllipticArc.defaultResult())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    self.assertTangentLineEllipse(SketchAPI_Line(self.myControlLines[0]), anEllipticArc)
+
+  def test_spline_tangent_coincident_by_aux(self):
+    """ Test 19. Set tangency between two B-spline curves coincident with B-spline start point
+    """
+    aSpline = self.mySketch.addSpline(poles = [(50, -20), (40, 0), (50, 20)])
+    self.myNbBSplines += 1
+    self.myDOF += aSpline.poles().size() * 2
+    model.do()
+
+    self.mySketch.setCoincident(SketchAPI_Point(self.myControlPoles[0]).coordinates(), aSpline.startPoint())
+    self.myNbCoincidence += 1
+    self.myDOF -= 2
+    model.do()
+
+    self.mySketch.setTangent(self.mySpline.result(), aSpline.result())
+    self.myNbTangency += 1
+    self.myDOF -= 1
+    model.do()
+
+    #self.assertPointLineDistance(aSpline.poles().pnt(1), self.myControlLines[0])
+    self.myExpectedFailure = True
+
+
+
+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/TestCreateBSpline.py b/src/SketchPlugin/Test/TestCreateBSpline.py
new file mode 100644 (file)
index 0000000..0aa12ee
--- /dev/null
@@ -0,0 +1,195 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test creation of B-spline curve
+"""
+
+import unittest
+from salome.shaper import model
+
+from GeomAPI import *
+from SketchAPI import *
+
+__updated__ = "2020-01-17"
+
+class TestBSpline(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myPoles = [GeomAPI_Pnt2d(50., 50.), GeomAPI_Pnt2d(70., 70.), GeomAPI_Pnt2d(80., 30.), GeomAPI_Pnt2d(50., 10.), GeomAPI_Pnt2d(10., -30.)]
+    self.myPolesCoordinates = [(50., 50.), (70., 70.), (80., 30.), (50., 10.), (10., -30.)]
+    self.myDegree = 3;
+    self.myDOF = 0
+    self.myNbPoints = 0
+    self.myNbLines = 0
+    self.myNbSplines = 0
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines)
+    model.testNbSubFeatures(self.mySketch, "SketchBSpline", self.myNbSplines)
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+
+  def test_bspline_by_coordinates(self):
+    """ Test 1. Create B-spline curve by coordinates of its poles
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPolesCoordinates)
+    self.myDOF += len(self.myPolesCoordinates) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_poles(self):
+    """ Test 2. Create B-spline curve by poles
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles)
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_degree_and_poles(self):
+    """ Test 3. Create B-spline curve by poles and degree
+    """
+    self.myDegree = 4
+    self.mySpline = self.mySketch.addSpline(degree = self.myDegree, poles = self.myPoles)
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_poles_and_weights(self):
+    """ Test 4. Create B-spline curve by poles and weights
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = [1, 2, 3, 2, 1])
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_parametric(self):
+    """ Test 5. Create B-spline curve by whole set of parameters
+    """
+    self.myDegree = 5
+    self.myPolesCoordinates = [(-79.8578274581199, 75.5284518447357),
+                               (-64.6205376770376, 62.7428476092882),
+                               (-49.3832478959552, 49.9572433738407),
+                               (-34.1459581148729, 37.1716391383932),
+                               (-18.9086683337906, 24.3860349029457),
+                               (-3.55842111132817, 11.5056481200973),
+                               (-3.37993197286247, 11.42995541724),
+                               (-3.1778022626919, 11.4565662984205),
+                               (-3.02498570721059, 11.575876223351),
+                               (8.46852511720001, 27.9903107977019),
+                               (19.8774589601206, 44.2839569245217),
+                               (31.2863928030413, 60.5776030513415),
+                               (42.6953266459619, 76.8712491781612),
+                               (54.1042604888826, 93.164895304981)
+                              ]
+    self.mySpline = self.mySketch.addSpline(degree = self.myDegree,
+                                            poles = self.myPolesCoordinates,
+                                            weights = [1, 1, 1, 1, 1, 1, 0.957903314642061, 0.95790331464206, 1, 1, 1, 1, 1, 1],
+                                            knots = [-494.543457494654, 500, 507.372773368102, 1501.91623086297],
+                                            multiplicities = [6, 4, 4, 6])
+    self.myDOF += len(self.myPolesCoordinates) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_linear(self):
+    """ Test 6. Create B-spline curve by 2 poles
+    """
+    self.myDegree = 1
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:2])
+    self.myDOF += 4
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_parabola(self):
+    """ Test 7. Create B-spline curve by 3 poles
+    """
+    self.myDegree = 2
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:3])
+    self.myDOF += 6
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_with_poles(self):
+    """ Test 8. Create B-spline curve and points coincident with its poles
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles)
+    self.mySpline.controlPoles(regular = [0, 2], auxiliary = [1, 3])
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    self.myNbPoints += 4
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_with_polygon(self):
+    """ Test 9. Create B-spline curve and its control polygon
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles)
+    self.mySpline.controlPolygon(regular = [0, 2], auxiliary = [1, 3])
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    self.myNbLines += 4
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+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/TestCreateBSplinePeriodic.py b/src/SketchPlugin/Test/TestCreateBSplinePeriodic.py
new file mode 100644 (file)
index 0000000..7cec4b0
--- /dev/null
@@ -0,0 +1,184 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test creation of periodic B-spline curve
+"""
+
+import unittest
+from salome.shaper import model
+
+from GeomAPI import *
+from SketchAPI import *
+
+__updated__ = "2020-01-24"
+
+class TestBSplinePeriodic(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myPoles = [GeomAPI_Pnt2d(50., 50.), GeomAPI_Pnt2d(70., 70.), GeomAPI_Pnt2d(80., 30.), GeomAPI_Pnt2d(50., 10.), GeomAPI_Pnt2d(10., -30.)]
+    self.myPolesCoordinates = [(50., 50.), (70., 70.), (80., 30.), (50., 10.), (10., -30.)]
+    self.myDegree = 3;
+    self.myDOF = 0
+    self.myNbPoints = 0
+    self.myNbLines = 0
+    self.myNbSplines = 0
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines)
+    model.testNbSubFeatures(self.mySketch, "SketchBSplinePeriodic", self.myNbSplines)
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+
+  def test_bspline_by_coordinates(self):
+    """ Test 1. Create B-spline curve by coordinates of its poles
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPolesCoordinates, periodic = True)
+    self.myDOF += len(self.myPolesCoordinates) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_poles(self):
+    """ Test 2. Create B-spline curve by poles
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True)
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_degree_and_poles(self):
+    """ Test 3. Create B-spline curve by poles and degree
+    """
+    self.myDegree = 4
+    self.mySpline = self.mySketch.addSpline(degree = self.myDegree, poles = self.myPoles, periodic = True)
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_poles_and_weights(self):
+    """ Test 4. Create B-spline curve by poles and weights
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, weights = [1, 2, 3, 2, 1], periodic = True)
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_by_parametric(self):
+    """ Test 5. Create B-spline curve by whole set of parameters
+    """
+    self.myDegree = 3
+    self.myPolesCoordinates = [(-10, 0), (-20, 20), (0, 10), (20, 20),
+                               (10, 0), (20, -20), (0, -10), (-20, -20)
+                              ]
+    self.mySpline = self.mySketch.addSpline(degree = self.myDegree,
+                                            poles = self.myPolesCoordinates,
+                                            weights = [1, 1, 1, 1, 1, 1, 1, 1],
+                                            knots = [0, 1, 2, 3, 4, 5, 6, 7, 8],
+                                            multiplicities = [1, 1, 1, 1, 1, 1, 1, 1, 1],
+                                            periodic = True)
+    self.myDOF += len(self.myPolesCoordinates) * 2
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_linear(self):
+    """ Test 6. Create B-spline curve by 2 poles
+    """
+    self.myDegree = 1
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:2], periodic = True)
+    self.myDOF += 4
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_parabola(self):
+    """ Test 7. Create B-spline curve by 3 poles
+    """
+    self.myDegree = 2
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles[:3], periodic = True)
+    self.myDOF += 6
+    self.myNbSplines += 1
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_with_poles(self):
+    """ Test 8. Create B-spline curve and points coincident with its poles
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True)
+    self.mySpline.controlPoles(regular = [0, 2], auxiliary = [1, 3])
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    self.myNbPoints += 4
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_with_polygon(self):
+    """ Test 9. Create B-spline curve and its control polygon
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True)
+    self.mySpline.controlPolygon(regular = [0, 2], auxiliary = [1, 3])
+    self.myDOF += len(self.myPoles) * 2
+    self.myNbSplines += 1
+    self.myNbLines += 4
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+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/TestCreateMacroBSpline.py b/src/SketchPlugin/Test/TestCreateMacroBSpline.py
new file mode 100644 (file)
index 0000000..478ef97
--- /dev/null
@@ -0,0 +1,163 @@
+# Copyright (C) 2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test creation of B-spline curve by references to another features
+"""
+
+import unittest
+from salome.shaper import model
+
+from GeomAPI import *
+from GeomDataAPI import *
+from ModelAPI import *
+from SketchAPI import *
+
+__updated__ = "2020-01-31"
+
+class TestMacroBSpline(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myPoint = self.mySketch.addPoint(50., 50.)
+    self.myLine = self.mySketch.addLine(30., -1., 70., 19.)
+
+    self.myPoles = [[GeomAPI_Pnt2d(50., 50.), self.myPoint.coordinates()],
+                    GeomAPI_Pnt2d(70., 70.),
+                    (80., 30.),
+                    [GeomAPI_Pnt2d(50., 10.), self.myLine.result()],
+                    GeomAPI_Pnt2d(20., 10.)
+                   ]
+
+    self.myDegree = 3;
+    self.myDOF = 6
+    self.myNbPoints = 1
+    self.myNbLines = 1
+    self.myNbSplines = 0
+    self.myNbSplinesP = 0
+    self.myNbCoincidences = 0
+    self.myNbInternal = 0
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+    model.testNbSubFeatures(self.mySketch, "SketchPoint", self.myNbPoints)
+    model.testNbSubFeatures(self.mySketch, "SketchLine", self.myNbLines)
+    model.testNbSubFeatures(self.mySketch, "SketchBSpline", self.myNbSplines)
+    model.testNbSubFeatures(self.mySketch, "SketchBSplinePeriodic", self.myNbSplinesP)
+    model.testNbSubFeatures(self.mySketch, "SketchMacroBSpline", 0)
+    model.testNbSubFeatures(self.mySketch, "SketchMacroBSplinePeriodic", 0)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidence", self.myNbCoincidences)
+    model.testNbSubFeatures(self.mySketch, "SketchConstraintCoincidenceInternal", self.myNbInternal)
+
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+
+  def test_bspline_non_periodic(self):
+    """ Test 1. Create B-spline curve by poles and references to other shapes
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles)
+    self.myDOF += len(self.myPoles) * 2 - 3
+    self.myNbSplines += 1
+    self.myNbPoints += len(self.myPoles)
+    self.myNbLines += len(self.myPoles) - 1
+    self.myNbCoincidences += 2
+    self.myNbInternal += len(self.myPoles) * 3 - 2
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_periodic(self):
+    """ Test 2. Create periodic B-spline curve by poles and references to other shapes
+    """
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True)
+    self.myDOF += len(self.myPoles) * 2 - 3
+    self.myNbSplinesP += 1
+    self.myNbPoints += len(self.myPoles)
+    self.myNbLines += len(self.myPoles)
+    self.myNbCoincidences += 2
+    self.myNbInternal += len(self.myPoles) * 3
+    model.do()
+
+    assert(self.mySpline.feature())
+    assert(self.mySpline.feature().error() == "")
+    assert(self.mySpline.degree().value() == self.myDegree)
+
+  def test_bspline_lowlevel(self):
+    """ Test 3. Create macro B-spline on low-level to test attributeRefAttrList
+    """
+    model.end()
+    session = ModelAPI_Session.get()
+    sketch = featureToCompositeFeature(self.mySketch.feature())
+    session.startOperation()
+    bspline = sketch.addFeature("SketchMacroBSpline")
+    poles = geomDataAPI_Point2DArray(bspline.attribute("poles"))
+    weights = bspline.data().realArray("weights")
+    polesRef = bspline.data().refattrlist("poles_ref")
+    bspline.boolean("need_control_poly").setValue(False)
+
+    poles.setSize(3); weights.setSize(3)
+    poles.setPnt(0, GeomAPI_Pnt2d(50., 50.)); weights.setValue(0, 1.0); polesRef.append(self.myPoint.coordinates())
+    poles.setPnt(1, GeomAPI_Pnt2d(50., 10.)); weights.setValue(1, 1.0); polesRef.append(self.myLine.defaultResult())
+    poles.setPnt(2, GeomAPI_Pnt2d(20., 10.)); weights.setValue(2, 1.0); polesRef.append(None)
+
+    assert(polesRef.isInList(self.myPoint.coordinates()))
+    assert(not polesRef.isInList(self.myLine.startPoint()))
+    assert(polesRef.isInList(self.myLine.defaultResult()))
+    assert(not polesRef.isInList(self.myPoint.defaultResult()))
+
+    assert(polesRef.isAttribute(0))
+    assert(not polesRef.isAttribute(1))
+    assert(not polesRef.isAttribute(2))
+    assert(not polesRef.isAttribute(3))
+
+    assert(polesRef.attribute(0) is not None)
+    assert(polesRef.attribute(1) is None)
+    assert(polesRef.attribute(2) is None)
+    assert(polesRef.attribute(3) is None)
+    assert(polesRef.object(0) is not None)
+    assert(polesRef.object(1) is not None)
+    assert(polesRef.object(2) is None)
+    assert(polesRef.object(3) is None)
+
+    polesRef.remove(self.myPoint.coordinates())
+    polesRef.remove(self.myLine.defaultResult())
+    assert(polesRef.size() == 1)
+    polesRef.removeLast()
+
+    polesRef.append(self.myPoint.coordinates())
+    polesRef.append(None)
+    polesRef.append(self.myLine.defaultResult())
+    polesRef.removeLast()
+    polesRef.append(self.myLine.defaultResult())
+
+    session.finishOperation()
+    model.begin()
+    self.myDOF += 6
+    self.myNbSplines += 1
+
+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/TestMoveBSpline.py b/src/SketchPlugin/Test/TestMoveBSpline.py
new file mode 100644 (file)
index 0000000..3e01ac7
--- /dev/null
@@ -0,0 +1,418 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test movement of the B-spline curve
+"""
+
+import unittest
+import math
+from GeomAPI import *
+from GeomDataAPI import geomDataAPI_Point2DArray
+from SketchAPI import *
+from salome.shaper import model
+
+__updated__ = "2020-01-20"
+
+class TestMoveBSpline(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myPoles = [GeomAPI_Pnt2d(20., 50.),
+                    GeomAPI_Pnt2d(70., 70.),
+                    GeomAPI_Pnt2d(80., 30.),
+                    GeomAPI_Pnt2d(50., 10.),
+                    GeomAPI_Pnt2d(90., -30.)]
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles)
+    self.myDOF = len(self.myPoles) * 2
+    model.do()
+    self.checkDOF()
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkPointCoordinates(self, thePoint, theCoordinates):
+    aCoord = []
+    if issubclass(type(theCoordinates), GeomAPI_Pnt2d):
+      aCoord = [theCoordinates.x(), theCoordinates.y()]
+    else:
+      aCoord = theCoordinates
+    DIGITS = 7 - math.floor(math.log10(math.hypot(aCoord[0], aCoord[1])))
+    self.assertAlmostEqual(thePoint.x(), aCoord[0], DIGITS)
+    self.assertAlmostEqual(thePoint.y(), aCoord[1], DIGITS)
+
+  def checkPoles(self, theBSpline, theCoordinates):
+    poles = theBSpline.poles()
+    for index, point in zip(range(0, len(theCoordinates)), theCoordinates):
+      self.checkPointCoordinates(poles.pnt(index), point)
+
+  def fixPoint(self, thePoint):
+    self.mySketch.setFixed(thePoint)
+    self.myDOF -= 2
+    model.do()
+    self.checkDOF()
+
+
+  def test_move_free_bspline(self):
+    """ Test 1. Movement of a free B-spline dragging the edge
+    """
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = []
+    for pole in self.myPoles:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_start_point(self):
+    """ Test 2. Movement of start point of a free B-spline curve
+    """
+    newPoles = self.myPoles
+
+    newPoles[0].setX(newPoles[0].x() - 20.)
+    newPoles[0].setY(newPoles[0].y() + 10.)
+    self.mySketch.move(self.mySpline.startPoint(), newPoles[0])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_end_point(self):
+    """ Test 3. Movement of end point of a free B-spline curve
+    """
+    newPoles = self.myPoles
+
+    newPoles[-1].setX(newPoles[-1].x() + 20.)
+    newPoles[-1].setY(newPoles[-1].y() + 10.)
+    self.mySketch.move(self.mySpline.endPoint(), newPoles[-1])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+
+  def test_move_bspline_with_start_point_fixed(self):
+    """ Test 4. Movement of a B-spline dragging the edge when start point is fixed
+    """
+    self.fixPoint(self.mySpline.startPoint())
+    model.do()
+
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = [self.myPoles[0]]
+    for pole in self.myPoles[1:]:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_start_point_with_start_point_fixed(self):
+    """ Test 5. Movement of start point of a free B-spline curve has no result if the first pole is fixed
+    """
+    self.fixPoint(self.mySpline.startPoint())
+    model.do()
+
+    self.mySketch.move(self.mySpline.startPoint(), self.myPoles[0].x() - 10., self.myPoles[0].y() + 10.)
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+  def test_move_end_point_with_start_point_fixed(self):
+    """ Test 6. Movement of end point of a free B-spline curve when start point is fixed
+    """
+    self.fixPoint(self.mySpline.startPoint())
+    model.do()
+
+    newPoles = self.myPoles
+
+    newPoles[-1].setX(newPoles[-1].x() + 20.)
+    newPoles[-1].setY(newPoles[-1].y() + 10.)
+    self.mySketch.move(self.mySpline.endPoint(), newPoles[-1])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+
+  def test_move_bspline_with_end_point_fixed(self):
+    """ Test 7. Movement of a B-spline dragging the edge when end point is fixed
+    """
+    self.fixPoint(self.mySpline.endPoint())
+    model.do()
+
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = []
+    for pole in self.myPoles[:-1]:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    newPoles.append(self.myPoles[-1])
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_start_point_with_end_point_fixed(self):
+    """ Test 8. Movement of start point of a free B-spline curve when end point is fixed
+    """
+    self.fixPoint(self.mySpline.endPoint())
+    model.do()
+
+    newPoles = self.myPoles
+
+    newPoles[0].setX(newPoles[0].x() + 20.)
+    newPoles[0].setY(newPoles[0].y() + 10.)
+    self.mySketch.move(self.mySpline.startPoint(), self.myPoles[0])
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+  def test_move_end_point_with_end_point_fixed(self):
+    """ Test 9. Movement of end point of a free B-spline curve has no result if the last pole is fixed
+    """
+    self.fixPoint(self.mySpline.endPoint())
+    model.do()
+
+    self.mySketch.move(self.mySpline.endPoint(), self.myPoles[-1].x() + 10., self.myPoles[-1].y() + 10.)
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+
+  def test_move_fixed_bspline(self):
+    """ Test 10. Movement of a fully fixed B-spline
+    """
+    self.mySketch.setFixed(self.mySpline.defaultResult())
+    self.myDOF = 0
+    model.do()
+
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+  def test_move_start_point_of_fixed_bspline(self):
+    """ Test 11. Movement of start point of a fully fixed B-spline curve
+    """
+    self.mySketch.setFixed(self.mySpline.defaultResult())
+    self.myDOF = 0
+    model.do()
+
+    self.mySketch.move(self.mySpline.startPoint(), self.myPoles[0].x() + 10., self.myPoles[0].y() + 10.)
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+  def test_move_end_point_of_fixed_bspline(self):
+    """ Test 12. Movement of end point of a fully fixed B-spline curve
+    """
+    self.mySketch.setFixed(self.mySpline.defaultResult())
+    self.myDOF = 0
+    model.do()
+
+    self.mySketch.move(self.mySpline.endPoint(), self.myPoles[-1].x() + 10., self.myPoles[-1].y() + 10.)
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+
+  def test_move_bspline_with_fixed_pole(self):
+    """ Test 13. Movement of a B-spline curve with fixed pole
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    model.do()
+
+    self.fixPoint(Point_2.defaultResult())
+    model.do()
+
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = []
+    for pole in self.myPoles:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    newPoles[1].setX(newPoles[1].x() - dx)
+    newPoles[1].setY(newPoles[1].y() - dy)
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_start_point_with_fixed_pole(self):
+    """ Test 14. Movement of start point of a B-spline curve with fixed pole
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    model.do()
+
+    self.fixPoint(Point_2.defaultResult())
+    model.do()
+
+    newPoles = self.myPoles
+
+    newPoles[0].setX(newPoles[0].x() + 20.)
+    newPoles[0].setY(newPoles[0].y() + 10.)
+    self.mySketch.move(self.mySpline.startPoint(), newPoles[0])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_end_point_with_fixed_pole(self):
+    """ Test 15. Movement of end point of a B-spline curve with fixed pole
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    model.do()
+
+    self.fixPoint(Point_2.defaultResult())
+    model.do()
+
+    newPoles = self.myPoles
+
+    newPoles[-1].setX(newPoles[-1].x() + 20.)
+    newPoles[-1].setY(newPoles[-1].y() + 10.)
+    self.mySketch.move(self.mySpline.endPoint(), newPoles[-1])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+
+  def test_move_bspline_with_fixed_segment(self):
+    """ Test 16. Movement of a B-spline curve with fixed control segment
+    """
+    [Line_1, Line_2, Line_3, Line_4] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3])
+    model.do()
+
+    self.mySketch.setFixed(Line_1.defaultResult())
+    self.myDOF -= 4
+    model.do()
+
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = [self.myPoles[0], self.myPoles[1]]
+    for pole in self.myPoles[2:]:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_start_point_with_fixed_segment(self):
+    """ Test 17. Movement of start point of a B-spline curve with fixed control segment
+    """
+    [Line_1, Line_2, Line_3, Line_4] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3])
+    model.do()
+
+    self.mySketch.setFixed(Line_1.defaultResult())
+    self.myDOF -= 4
+    model.do()
+
+    self.mySketch.move(self.mySpline.startPoint(), self.myPoles[0].x() + 10., self.myPoles[0].y() - 20.)
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+  def test_move_end_point_with_fixed_segment(self):
+    """ Test 18. Movement of end point of a B-spline curve with fixed control segment
+    """
+    [Line_1, Line_2, Line_3, Line_4] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3])
+    model.do()
+
+    self.mySketch.setFixed(Line_1.defaultResult())
+    self.myDOF -= 4
+    model.do()
+
+    newPoles = self.myPoles
+
+    newPoles[-1].setX(newPoles[-1].x() + 20.)
+    newPoles[-1].setY(newPoles[-1].y() + 10.)
+    self.mySketch.move(self.mySpline.endPoint(), newPoles[-1])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+
+  def test_move_pole_of_free_bspline(self):
+    """ Test 19. Movement of a pole of a B-spline curve
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    [Line_1, Line_2, Line_3, Line_4] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3])
+    model.do()
+
+    newPoles = self.myPoles
+
+    newPoles[2].setX(newPoles[2].x() + 20.)
+    newPoles[2].setY(newPoles[2].y() + 20.)
+    self.mySketch.move(SketchAPI_Point(Point_3).coordinates(), newPoles[2])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_segment_of_free_bspline(self):
+    """ Test 20. Movement of a control segment of a B-spline curve
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    [Line_1, Line_2, Line_3, Line_4] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3])
+    model.do()
+
+    oldPosition = GeomAPI_Pnt2d(0.5 * (self.myPoles[2].x() + self.myPoles[3].x()),
+                                0.5 * (self.myPoles[2].y() + self.myPoles[3].y()))
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(SketchAPI_Line(Line_3).defaultResult(), newPosition)
+    model.do()
+
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = self.myPoles
+    newPoles[2].setX(newPoles[2].x() + dx)
+    newPoles[2].setY(newPoles[2].y() + dy)
+    newPoles[3].setX(newPoles[3].x() + dx)
+    newPoles[3].setY(newPoles[3].y() + dy)
+
+    self.checkPoles(self.mySpline, newPoles)
+
+
+
+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/TestMoveBSplinePeriodic.py b/src/SketchPlugin/Test/TestMoveBSplinePeriodic.py
new file mode 100644 (file)
index 0000000..d7c702a
--- /dev/null
@@ -0,0 +1,205 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test movement of the periodic B-spline curve
+"""
+
+import unittest
+import math
+from GeomAPI import *
+from GeomDataAPI import geomDataAPI_Point2DArray
+from SketchAPI import *
+from salome.shaper import model
+
+__updated__ = "2020-01-20"
+
+class TestMoveBSpline(unittest.TestCase):
+  def setUp(self):
+    model.begin()
+    self.myDocument = model.moduleDocument()
+    self.mySketch = model.addSketch(self.myDocument, model.defaultPlane("XOY"))
+    self.myPoles = [GeomAPI_Pnt2d(20., 50.),
+                    GeomAPI_Pnt2d(70., 70.),
+                    GeomAPI_Pnt2d(80., 30.),
+                    GeomAPI_Pnt2d(50., -10.),
+                    GeomAPI_Pnt2d(90., -30.)]
+    self.mySpline = self.mySketch.addSpline(poles = self.myPoles, periodic = True)
+    self.myDOF = len(self.myPoles) * 2
+    model.do()
+    self.checkDOF()
+
+  def tearDown(self):
+    self.checkDOF()
+    model.end()
+
+  def checkDOF(self):
+    self.assertEqual(model.dof(self.mySketch), self.myDOF)
+
+  def checkPointCoordinates(self, thePoint, theCoordinates):
+    aCoord = []
+    if issubclass(type(theCoordinates), GeomAPI_Pnt2d):
+      aCoord = [theCoordinates.x(), theCoordinates.y()]
+    else:
+      aCoord = theCoordinates
+    DIGITS = 7 - math.floor(math.log10(math.hypot(aCoord[0], aCoord[1])))
+    self.assertAlmostEqual(thePoint.x(), aCoord[0], DIGITS)
+    self.assertAlmostEqual(thePoint.y(), aCoord[1], DIGITS)
+
+  def checkPoles(self, theBSpline, theCoordinates):
+    poles = theBSpline.poles()
+    for index, point in zip(range(0, len(theCoordinates)), theCoordinates):
+      self.checkPointCoordinates(poles.pnt(index), point)
+
+  def fixPoint(self, thePoint):
+    self.mySketch.setFixed(thePoint)
+    self.myDOF -= 2
+    model.do()
+    self.checkDOF()
+
+
+  def test_move_free_bspline(self):
+    """ Test 1. Movement of a free B-spline dragging the edge
+    """
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = []
+    for pole in self.myPoles:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    self.checkPoles(self.mySpline, newPoles)
+
+
+  def test_move_fixed_bspline(self):
+    """ Test 2. Movement of a fully fixed B-spline
+    """
+    self.mySketch.setFixed(self.mySpline.defaultResult())
+    self.myDOF = 0
+    model.do()
+
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    self.checkPoles(self.mySpline, self.myPoles)
+
+
+  def test_move_bspline_with_fixed_pole(self):
+    """ Test 3. Movement of a B-spline curve with fixed pole
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    model.do()
+
+    self.fixPoint(Point_2.defaultResult())
+    model.do()
+
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = []
+    for pole in self.myPoles:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    newPoles[1].setX(newPoles[1].x() - dx)
+    newPoles[1].setY(newPoles[1].y() - dy)
+    self.checkPoles(self.mySpline, newPoles)
+
+
+  def test_move_bspline_with_fixed_segment(self):
+    """ Test 4. Movement of a B-spline curve with fixed control segment
+    """
+    [Line_1, Line_2, Line_3, Line_4, Line_5] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3, 4])
+    model.do()
+
+    self.mySketch.setFixed(Line_1.defaultResult())
+    self.myDOF -= 4
+    model.do()
+
+    oldPosition = GeomAPI_Edge(self.mySpline.defaultResult().shape()).middlePoint()
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(self.mySpline.defaultResult(), newPosition)
+    model.do()
+
+    # plane is XOY, no need to project oldPosition point
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = [self.myPoles[0], self.myPoles[1]]
+    for pole in self.myPoles[2:]:
+      newPoles.append(GeomAPI_Pnt2d(pole.x() + dx, pole.y() + dy))
+    self.checkPoles(self.mySpline, newPoles)
+
+
+  def test_move_pole_of_free_bspline(self):
+    """ Test 5. Movement of a pole of a B-spline curve
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    [Line_1, Line_2, Line_3, Line_4, Line_5] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3, 4])
+    model.do()
+
+    newPoles = self.myPoles
+
+    newPoles[2].setX(newPoles[2].x() + 20.)
+    newPoles[2].setY(newPoles[2].y() + 20.)
+    self.mySketch.move(SketchAPI_Point(Point_3).coordinates(), newPoles[2])
+    model.do()
+
+    self.checkPoles(self.mySpline, newPoles)
+
+  def test_move_segment_of_free_bspline(self):
+    """ Test 6. Movement of a control segment of a B-spline curve
+    """
+    [Point_1, Point_2, Point_3, Point_4, Point_5] = self.mySpline.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+    [Line_1, Line_2, Line_3, Line_4, Line_5] = self.mySpline.controlPolygon(auxiliary = [0, 1, 2, 3, 4])
+    model.do()
+
+    oldPosition = GeomAPI_Pnt2d(0.5 * (self.myPoles[2].x() + self.myPoles[3].x()),
+                                0.5 * (self.myPoles[2].y() + self.myPoles[3].y()))
+    newPosition = GeomAPI_Pnt2d(120., 90.)
+    self.mySketch.move(SketchAPI_Line(Line_3).defaultResult(), newPosition)
+    model.do()
+
+    dx = newPosition.x() - oldPosition.x()
+    dy = newPosition.y() - oldPosition.y()
+
+    newPoles = self.myPoles
+    newPoles[2].setX(newPoles[2].x() + dx)
+    newPoles[2].setY(newPoles[2].y() + dy)
+    newPoles[3].setX(newPoles[3].x() + dx)
+    newPoles[3].setY(newPoles[3].y() + dy)
+
+    self.checkPoles(self.mySpline, newPoles)
+
+
+
+if __name__ == "__main__":
+    test_program = unittest.main(exit=False)
+    assert test_program.result.wasSuccessful(), "Test failed"
+    assert model.checkPythonDump()
index b3400e70e822c20d0f326ab14451918e04fec697..2f09005efa5edcc31a874e5dc5b4c4cedabd3e7d 100644 (file)
@@ -156,3 +156,41 @@ anEllipticArcPnt3.setValue(0, 5)
 anEllipticArcPnt4.setValue(-10, 0)
 assert(featureToPresentation(anEllipticArc).getAISObject(None) is not None)
 aSession.finishOperation()
+
+# Test presentation for MacroBSpline on low-level
+aSession.startOperation()
+aBSpline = aSketchFeature.addFeature("SketchMacroBSpline")
+aPoles = geomDataAPI_Point2DArray(aBSpline.attribute("poles"))
+aPoles.setSize(4)
+aPoles.setPnt(0, 0, 0)
+aPoles.setPnt(1, 10, 0)
+aPoles.setPnt(2, 10, 10)
+aPoles.setPnt(3, 0, 10)
+aWeights = aBSpline.data().realArray("weights")
+aWeights.setSize(4)
+aWeights.setValue(0, 1)
+aWeights.setValue(1, 2)
+aWeights.setValue(2, 2)
+aWeights.setValue(3, 1)
+aBSpline.boolean("need_control_poly").setValue(True)
+assert(featureToPresentation(aBSpline).getAISObject(None) is not None)
+aSession.finishOperation()
+
+# Test presentation for MacroBSplinePeriodic on low-level
+aSession.startOperation()
+aBSplineP = aSketchFeature.addFeature("SketchMacroBSplinePeriodic")
+aPoles = geomDataAPI_Point2DArray(aBSplineP.attribute("poles"))
+aPoles.setSize(4)
+aPoles.setPnt(0, 0, 0)
+aPoles.setPnt(1, 10, 0)
+aPoles.setPnt(2, 10, 10)
+aPoles.setPnt(3, 0, 10)
+aWeights = aBSplineP.data().realArray("weights")
+aWeights.setSize(4)
+aWeights.setValue(0, 1)
+aWeights.setValue(1, 2)
+aWeights.setValue(2, 2)
+aWeights.setValue(3, 1)
+aBSplineP.boolean("need_control_poly").setValue(True)
+assert(featureToPresentation(aBSplineP).getAISObject(None) is not None)
+aSession.finishOperation()
diff --git a/src/SketchPlugin/Test/TestProjectionBSpline.py b/src/SketchPlugin/Test/TestProjectionBSpline.py
new file mode 100644 (file)
index 0000000..8e651ba
--- /dev/null
@@ -0,0 +1,78 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Point_2 = model.addPoint(Part_1_doc, -10, 5, 10)
+Point_3 = model.addPoint(Part_1_doc, -5, 10, 15)
+Point_4 = model.addPoint(Part_1_doc, 10, 0, 20)
+Point_5 = model.addPoint(Part_1_doc, 10, -10, 15)
+Point_6 = model.addPoint(Part_1_doc, -5, -5, 12)
+Interpolation_1_objects = [model.selection("VERTEX", "Point_1"), model.selection("VERTEX", "Point_2"), model.selection("VERTEX", "Point_3"), model.selection("VERTEX", "Point_4"), model.selection("VERTEX", "Point_5")]
+Interpolation_1 = model.addInterpolation(Part_1_doc, Interpolation_1_objects, False, False)
+
+Sketch_2 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchProjection_1 = Sketch_2.addProjection(model.selection("EDGE", "Interpolation_1_1"), True)
+SketchBSpline_1 = SketchProjection_1.createdFeature()
+model.do()
+
+Sketch_3 = model.addSketch(Part_1_doc, model.standardPlane("XOZ"))
+SketchProjection_2 = Sketch_3.addProjection(model.selection("EDGE", "Interpolation_1_1"), True)
+SketchBSpline_2 = SketchProjection_2.createdFeature()
+model.do()
+
+Sketch_4 = model.addSketch(Part_1_doc, model.standardPlane("YOZ"))
+SketchProjection_3 = Sketch_4.addProjection(model.selection("EDGE", "Interpolation_1_1"), True)
+SketchBSpline_3 = SketchProjection_3.createdFeature()
+model.do()
+
+model.end()
+
+from GeomAPI import *
+import math
+
+TOLERANCE = 1.e-7
+
+def checkProjection(theBSpline3D, theBSpline2D, theFlags):
+    assert(theBSpline2D.isEdge() and theBSpline2D.edge().isBSpline())
+    poles2D = GeomAPI_BSpline(GeomAPI_Curve(theBSpline2D)).poles()
+    poles3D = theBSpline3D.poles()
+    assert(poles2D.size() == poles3D.size())
+    for p2d, p3d in zip(poles2D, poles3D):
+        assert(math.fabs((p2d.x() - p3d.x()) * theFlags.x()) < TOLERANCE and
+               math.fabs((p2d.y() - p3d.y()) * theFlags.y()) < TOLERANCE and
+               math.fabs((p2d.z() - p3d.z()) * theFlags.z()) < TOLERANCE)
+
+
+bspline0 = GeomAPI_BSpline(GeomAPI_Curve(Interpolation_1.results()[-1].resultSubShapePair()[0].shape()))
+
+bsplineShape1 = SketchBSpline_1.results()[-1].resultSubShapePair()[0].shape()
+checkProjection(bspline0, bsplineShape1, GeomAPI_Pnt(1, 1, 0))
+
+bsplineShape2 = SketchBSpline_2.results()[-1].resultSubShapePair()[0].shape()
+checkProjection(bspline0, bsplineShape2, GeomAPI_Pnt(1, 0, 1))
+
+bsplineShape3 = SketchBSpline_3.results()[-1].resultSubShapePair()[0].shape()
+checkProjection(bspline0, bsplineShape3, GeomAPI_Pnt(0, 1, 1))
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestProjectionBSplinePeriodic.py b/src/SketchPlugin/Test/TestProjectionBSplinePeriodic.py
new file mode 100644 (file)
index 0000000..30ae551
--- /dev/null
@@ -0,0 +1,78 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Part_1 = model.addPart(partSet)
+Part_1_doc = Part_1.document()
+Point_2 = model.addPoint(Part_1_doc, -10, 5, 10)
+Point_3 = model.addPoint(Part_1_doc, -5, 10, 15)
+Point_4 = model.addPoint(Part_1_doc, 10, 0, 20)
+Point_5 = model.addPoint(Part_1_doc, 10, -10, 15)
+Point_6 = model.addPoint(Part_1_doc, -5, -5, 12)
+Interpolation_1_objects = [model.selection("VERTEX", "Point_1"), model.selection("VERTEX", "Point_2"), model.selection("VERTEX", "Point_3"), model.selection("VERTEX", "Point_4"), model.selection("VERTEX", "Point_5")]
+Interpolation_1 = model.addInterpolation(Part_1_doc, Interpolation_1_objects, True, False)
+
+Sketch_2 = model.addSketch(Part_1_doc, model.standardPlane("XOY"))
+SketchProjection_1 = Sketch_2.addProjection(model.selection("EDGE", "Interpolation_1_1"), True)
+SketchBSpline_1 = SketchProjection_1.createdFeature()
+model.do()
+
+Sketch_3 = model.addSketch(Part_1_doc, model.standardPlane("XOZ"))
+SketchProjection_2 = Sketch_3.addProjection(model.selection("EDGE", "Interpolation_1_1"), True)
+SketchBSpline_2 = SketchProjection_2.createdFeature()
+model.do()
+
+Sketch_4 = model.addSketch(Part_1_doc, model.standardPlane("YOZ"))
+SketchProjection_3 = Sketch_4.addProjection(model.selection("EDGE", "Interpolation_1_1"), True)
+SketchBSpline_3 = SketchProjection_3.createdFeature()
+model.do()
+
+model.end()
+
+from GeomAPI import *
+import math
+
+TOLERANCE = 1.e-7
+
+def checkProjection(theBSpline3D, theBSpline2D, theFlags):
+    assert(theBSpline2D.isEdge() and theBSpline2D.edge().isBSpline())
+    poles2D = GeomAPI_BSpline(GeomAPI_Curve(theBSpline2D)).poles()
+    poles3D = theBSpline3D.poles()
+    assert(poles2D.size() == poles3D.size())
+    for p2d, p3d in zip(poles2D, poles3D):
+        assert(math.fabs((p2d.x() - p3d.x()) * theFlags.x()) < TOLERANCE and
+               math.fabs((p2d.y() - p3d.y()) * theFlags.y()) < TOLERANCE and
+               math.fabs((p2d.z() - p3d.z()) * theFlags.z()) < TOLERANCE)
+
+
+bspline0 = GeomAPI_BSpline(GeomAPI_Curve(Interpolation_1.results()[-1].resultSubShapePair()[0].shape()))
+
+bsplineShape1 = SketchBSpline_1.results()[-1].resultSubShapePair()[0].shape()
+checkProjection(bspline0, bsplineShape1, GeomAPI_Pnt(1, 1, 0))
+
+bsplineShape2 = SketchBSpline_2.results()[-1].resultSubShapePair()[0].shape()
+checkProjection(bspline0, bsplineShape2, GeomAPI_Pnt(1, 0, 1))
+
+bsplineShape3 = SketchBSpline_3.results()[-1].resultSubShapePair()[0].shape()
+checkProjection(bspline0, bsplineShape3, GeomAPI_Pnt(0, 1, 1))
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestRemoveBSpline.py b/src/SketchPlugin/Test/TestRemoveBSpline.py
new file mode 100644 (file)
index 0000000..05da298
--- /dev/null
@@ -0,0 +1,108 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test removing B-spline curve and its construstion elements
+"""
+
+from salome.shaper import model
+from ModelAPI import *
+
+def assertNbSubs(theSketch, theNbPoints, theNbLines, theNbSplines, theNbInternalConstraints):
+    model.testNbSubFeatures(theSketch, "SketchPoint", theNbPoints)
+    model.testNbSubFeatures(theSketch, "SketchLine", theNbLines)
+    model.testNbSubFeatures(theSketch, "SketchBSpline", theNbSplines)
+    model.testNbSubFeatures(theSketch, "SketchConstraintCoincidenceInternal", theNbInternalConstraints)
+
+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"))
+SketchBSpline_1_poles = [(-30, -10), (-15, 20), (0, -10), (15, 20), (30, -10)]
+SketchBSpline_1 = Sketch_1.addSpline(poles = SketchBSpline_1_poles)
+controlPoles = SketchBSpline_1.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+controlLines = SketchBSpline_1.controlPolygon(auxiliary = [0, 1, 2, 3])
+model.do()
+model.end()
+
+DEFAULT_DOF = len(SketchBSpline_1_poles) * 2
+DEFAULT_POINTS = len(SketchBSpline_1_poles)
+DEFAULT_LINES = len(SketchBSpline_1_poles) - 1
+DEFAULT_BSPLINES = 1
+DEAFULT_INTERNALS = len(controlPoles) + len(controlLines) * 2
+
+assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 1. Remove auxiliary points one by one.
+for pnt in controlPoles:
+    model.begin()
+    removeFeaturesAndReferences(FeatureSet([pnt.feature()]))
+    model.end()
+
+    assertNbSubs(Sketch_1, DEFAULT_POINTS - 1, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 1)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+    model.undo()
+    assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 2. Remove auxiliary lines one by one.
+for ln in controlLines:
+    model.begin()
+    removeFeaturesAndReferences(FeatureSet([ln.feature()]))
+    model.end()
+
+    assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES - 1, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 2)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+    model.undo()
+    assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 3. Remove the B-spline curve.
+model.begin()
+removeFeaturesAndReferences(FeatureSet([SketchBSpline_1.feature()]))
+model.end()
+
+assertNbSubs(Sketch_1, 0, 0, 0, 0)
+assert(model.dof(Sketch_1) == 0)
+model.undo()
+assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 4. Remove some construction elements, make non-auxiliary a couple of the rest and check the dumping.
+model.begin()
+partSet = model.moduleDocument()
+Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchBSpline_2_poles = [(-30, -10), (-15, -40), (0, -10), (15, -40), (30, -10)]
+SketchBSpline_2 = Sketch_2.addSpline(poles = SketchBSpline_2_poles)
+controlPoles2 = SketchBSpline_2.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+controlLines2 = SketchBSpline_2.controlPolygon(auxiliary = [0, 1, 2, 3])
+model.do()
+model.end()
+
+model.begin()
+controlPoles2[1].setAuxiliary(False)
+controlLines2[2].setAuxiliary(False)
+removeFeaturesAndReferences(FeatureSet([controlPoles2[2].feature(), controlLines2[0].feature()]))
+model.end()
+
+assertNbSubs(Sketch_2, DEFAULT_POINTS - 1, DEFAULT_LINES - 1, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 3)
+
+assert(model.checkPythonDump())
diff --git a/src/SketchPlugin/Test/TestRemoveBSplinePeriodic.py b/src/SketchPlugin/Test/TestRemoveBSplinePeriodic.py
new file mode 100644 (file)
index 0000000..4e9d3eb
--- /dev/null
@@ -0,0 +1,108 @@
+# Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+    Test removing peridoc B-spline curve and its construstion elements
+"""
+
+from salome.shaper import model
+from ModelAPI import *
+
+def assertNbSubs(theSketch, theNbPoints, theNbLines, theNbSplines, theNbInternalConstraints):
+    model.testNbSubFeatures(theSketch, "SketchPoint", theNbPoints)
+    model.testNbSubFeatures(theSketch, "SketchLine", theNbLines)
+    model.testNbSubFeatures(theSketch, "SketchBSplinePeriodic", theNbSplines)
+    model.testNbSubFeatures(theSketch, "SketchConstraintCoincidenceInternal", theNbInternalConstraints)
+
+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"))
+SketchBSpline_1_poles = [(-30, -10), (-15, 20), (0, -10), (15, 20), (30, -10)]
+SketchBSpline_1 = Sketch_1.addSpline(poles = SketchBSpline_1_poles, periodic = True)
+controlPoles = SketchBSpline_1.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+controlLines = SketchBSpline_1.controlPolygon(auxiliary = [0, 1, 2, 3, 4])
+model.do()
+model.end()
+
+DEFAULT_DOF = len(SketchBSpline_1_poles) * 2
+DEFAULT_POINTS = len(SketchBSpline_1_poles)
+DEFAULT_LINES = len(SketchBSpline_1_poles)
+DEFAULT_BSPLINES = 1
+DEAFULT_INTERNALS = len(controlPoles) + len(controlLines) * 2
+
+assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 1. Remove auxiliary points one by one.
+for pnt in controlPoles:
+    model.begin()
+    removeFeaturesAndReferences(FeatureSet([pnt.feature()]))
+    model.end()
+
+    assertNbSubs(Sketch_1, DEFAULT_POINTS - 1, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 1)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+    model.undo()
+    assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 2. Remove auxiliary lines one by one.
+for ln in controlLines:
+    model.begin()
+    removeFeaturesAndReferences(FeatureSet([ln.feature()]))
+    model.end()
+
+    assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES - 1, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 2)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+    model.undo()
+    assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+    assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 3. Remove the B-spline curve.
+model.begin()
+removeFeaturesAndReferences(FeatureSet([SketchBSpline_1.feature()]))
+model.end()
+
+assertNbSubs(Sketch_1, 0, 0, 0, 0)
+assert(model.dof(Sketch_1) == 0)
+model.undo()
+assertNbSubs(Sketch_1, DEFAULT_POINTS, DEFAULT_LINES, DEFAULT_BSPLINES, DEAFULT_INTERNALS)
+assert(model.dof(Sketch_1) == DEFAULT_DOF)
+
+# Test 4. Remove some construction elements, make non-auxiliary a couple of the rest and check the dumping.
+model.begin()
+partSet = model.moduleDocument()
+Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY"))
+SketchBSpline_2_poles = [(-30, -10), (-15, -40), (0, -10), (15, -40), (30, -10)]
+SketchBSpline_2 = Sketch_2.addSpline(poles = SketchBSpline_2_poles, periodic = True)
+controlPoles2 = SketchBSpline_2.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+controlLines2 = SketchBSpline_2.controlPolygon(auxiliary = [0, 1, 2, 3, 4])
+model.do()
+model.end()
+
+model.begin()
+controlPoles2[1].setAuxiliary(False)
+controlLines2[2].setAuxiliary(False)
+removeFeaturesAndReferences(FeatureSet([controlPoles2[2].feature(), controlLines2[0].feature()]))
+model.end()
+
+assertNbSubs(Sketch_2, DEFAULT_POINTS - 1, DEFAULT_LINES - 1, DEFAULT_BSPLINES, DEAFULT_INTERNALS - 3)
+
+assert(model.checkPythonDump())
index 04a861a8102c4799d043c03271f80d30a20b0d71..6d2a5af6cdfc2b135adbb6e6e0c5ca79c503dad1 100644 (file)
@@ -92,6 +92,7 @@ The plug-in includes the following features for creation of 2D objects:
    arcFeature.rst
    ellipseFeature.rst
    arcEllipseFeature.rst
+   bsplineFeature.rst
 
 .. _sketch_constraints:
 
@@ -153,14 +154,14 @@ Sketcher comes into overconstrained state when an extra constraint was defined.
 .. centered::
   Overconstrained Sketch
 
-When Sketcher becomes into the state then:
+When Sketcher gets this state then:
 
 - buttons Apply and Cancel for the whole Sketcher are blocked;
 - a constraint which causes the overconstraint state is highlightid by red color;
 - a warning message in sketcher Property Panel is shown with recomendation what to do in this case;
 - an additional Undo button is shown under the warning messages;
 
-After undoing the last operation or deletion of a conflicting constraint Sketcher will become into a normal state.
+After undoing the last operation or deletion of a conflicting constraint Sketcher will be reverted into a normal state.
 
 Operations
 ----------
diff --git a/src/SketchPlugin/doc/TUI_bsplineFeature.rst b/src/SketchPlugin/doc/TUI_bsplineFeature.rst
new file mode 100644 (file)
index 0000000..962c33f
--- /dev/null
@@ -0,0 +1,11 @@
+
+  .. _tui_create_bspline:
+
+Create Sketch B-spline
+======================
+
+.. literalinclude:: examples/bspline.py
+    :linenos:
+    :language: python
+
+:download:`Download this script <examples/bspline.py>`
diff --git a/src/SketchPlugin/doc/bsplineFeature.rst b/src/SketchPlugin/doc/bsplineFeature.rst
new file mode 100644 (file)
index 0000000..5d8cde2
--- /dev/null
@@ -0,0 +1,81 @@
+.. |bspline.icon|    image:: images/bspline.png
+.. |bspline_p.icon|  image:: images/bspline_p.png
+.. |add_pole.icon|   image:: images/bspline_add_pole.png
+
+B-spline and periodic B-spline
+==============================
+
+The feature creates a free form spline curve in the current Sketch.
+
+To add a new B-spline to the Sketch:
+
+#. select in the Main Menu *Sketch - > B-spline* item  or
+#. click |bspline.icon| **B-spline** button in the Sketch toolbar.
+
+To add a periodic B-spline to the Sketch:
+
+#. select in the Main Menu *Sketch - > Periodic B-spline* item  or
+#. click |bspline_p.icon| **Periodic B-spline** button in the Sketch toolbar.
+
+
+Creation of B-spline curve
+""""""""""""""""""""""""""
+
+.. image:: images/bspline_creation_panel.png
+   :align: center
+
+Click in the view to specify the control polygon of B-spline curve. The curve will be shown after the second point is initialized. To stop adding new poles, click **Esc** button or **Apply** the operation.
+
+
+**TUI Command**:
+
+.. py:function:: Sketch_1.addSpline(degree, poles, weights, knots, multiplicities, periodic)
+
+    :param integer: degree of B-spline.
+    :param array: list of poles [(x1, y1), (x2, y2), ...].
+    :param array: list of weights for corresponding poles.
+    :param array: parametric knots of B-spline curve.
+    :param array: multiplicity of each knot.
+    :param boolean: True mentions that the B-spline curve is periodic.
+    :return: Result object.
+
+Each parameter is optional.
+
+Result
+""""""
+
+Created B-spline curve appears in the view.
+
+.. image:: images/bspline_result.png
+          :align: center
+
+.. centered::
+   Non-periodic B-spline created
+
+
+.. image:: images/bspline_periodic_result.png
+          :align: center
+
+.. centered::
+   Periodic B-spline created
+
+**See Also** a sample TUI Script of :ref:`tui_create_bspline` operation.
+
+
+Modification of B-spline curve
+""""""""""""""""""""""""""""""
+
+.. image:: images/bspline_modification_panel.png
+   :align: center
+
+.. centered::
+   Modification panel for B-spline curve
+
+The following options are provided to modify the already created B-spline curve:
+
+#. Change weight of each pole.
+#. Add new pole.
+
+The new pole is added after the current by pressing on the corresponding |add_pole.icon| button. The default weight for the new pole is 1. 
+
+**Note:** adding the new pole after the last for non-periodic B-spline will not change the last point of the curve to avoid modification of full sketch.
diff --git a/src/SketchPlugin/doc/examples/bspline.py b/src/SketchPlugin/doc/examples/bspline.py
new file mode 100644 (file)
index 0000000..40f5bb3
--- /dev/null
@@ -0,0 +1,18 @@
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY"))
+
+SketchBSpline_1_poles = [(-70, -5), (-50, 30), (-40, 3), (-20, 20), (-10, -5)]
+SketchBSpline_1 = Sketch_1.addSpline(poles = SketchBSpline_1_poles, weights = [1, 2, 2, 2, 1])
+[SketchPoint_1, SketchPoint_2, SketchPoint_3, SketchPoint_4, SketchPoint_5] = SketchBSpline_1.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+[SketchLine_1, SketchLine_2, SketchLine_3, SketchLine_4] = SketchBSpline_1.controlPolygon(auxiliary = [0, 1, 2, 3])
+
+SketchBSplinePeriodic_1_poles = [(10, -5), (30, 30), (40, 3), (60, 20), (70, -5)]
+SketchBSplinePeriodic_1 = Sketch_1.addSpline(poles = SketchBSplinePeriodic_1_poles, weights = [3, 2, 1, 1, 1], periodic = True)
+[SketchPoint_6, SketchPoint_7, SketchPoint_8, SketchPoint_9, SketchPoint_10] = SketchBSplinePeriodic_1.controlPoles(auxiliary = [0, 1, 2, 3, 4])
+[SketchLine_5, SketchLine_6, SketchLine_7, SketchLine_8, SketchLine_9] = SketchBSplinePeriodic_1.controlPolygon(auxiliary = [0, 1, 2, 3, 4])
+
+model.do()
+model.end()
diff --git a/src/SketchPlugin/doc/images/bspline.png b/src/SketchPlugin/doc/images/bspline.png
new file mode 100644 (file)
index 0000000..149d19e
Binary files /dev/null and b/src/SketchPlugin/doc/images/bspline.png differ
diff --git a/src/SketchPlugin/doc/images/bspline_add_pole.png b/src/SketchPlugin/doc/images/bspline_add_pole.png
new file mode 100644 (file)
index 0000000..388f8ff
Binary files /dev/null and b/src/SketchPlugin/doc/images/bspline_add_pole.png differ
diff --git a/src/SketchPlugin/doc/images/bspline_creation_panel.png b/src/SketchPlugin/doc/images/bspline_creation_panel.png
new file mode 100644 (file)
index 0000000..88acb19
Binary files /dev/null and b/src/SketchPlugin/doc/images/bspline_creation_panel.png differ
diff --git a/src/SketchPlugin/doc/images/bspline_modification_panel.png b/src/SketchPlugin/doc/images/bspline_modification_panel.png
new file mode 100644 (file)
index 0000000..4bda345
Binary files /dev/null and b/src/SketchPlugin/doc/images/bspline_modification_panel.png differ
diff --git a/src/SketchPlugin/doc/images/bspline_p.png b/src/SketchPlugin/doc/images/bspline_p.png
new file mode 100644 (file)
index 0000000..a23ade1
Binary files /dev/null and b/src/SketchPlugin/doc/images/bspline_p.png differ
diff --git a/src/SketchPlugin/doc/images/bspline_periodic_result.png b/src/SketchPlugin/doc/images/bspline_periodic_result.png
new file mode 100644 (file)
index 0000000..b02a7cc
Binary files /dev/null and b/src/SketchPlugin/doc/images/bspline_periodic_result.png differ
diff --git a/src/SketchPlugin/doc/images/bspline_result.png b/src/SketchPlugin/doc/images/bspline_result.png
new file mode 100644 (file)
index 0000000..9b954c0
Binary files /dev/null and b/src/SketchPlugin/doc/images/bspline_result.png differ
diff --git a/src/SketchPlugin/icons/bspline.png b/src/SketchPlugin/icons/bspline.png
new file mode 100644 (file)
index 0000000..149d19e
Binary files /dev/null and b/src/SketchPlugin/icons/bspline.png differ
diff --git a/src/SketchPlugin/icons/bspline_p.png b/src/SketchPlugin/icons/bspline_p.png
new file mode 100644 (file)
index 0000000..a23ade1
Binary files /dev/null and b/src/SketchPlugin/icons/bspline_p.png differ
index 0c77223de177eb7e0fabc366fd3c5e1002df68e6..408781f166c776275800dbd6df2631bce2db1b28 100644 (file)
@@ -6,6 +6,7 @@
         nested="SketchPoint SketchIntersectionPoint SketchLine
                 SketchCircle SketchMacroCircle SketchArc SketchMacroArc
                 SketchEllipse SketchMacroEllipse SketchEllipticArc SketchMacroEllipticArc
+                SketchBSpline SketchMacroBSpline SketchMacroBSplinePeriodic SketchBSplinePeriodic
                 SketchRectangle
                 SketchProjection
                 SketchConstraintLength SketchConstraintRadius SketchConstraintDistance SketchConstraintDistanceHorizontal SketchConstraintDistanceVertical
                                      tooltip="Passed point coordinates"
                                      accept_expressions="0"
                                      enable_value="enable_by_preferences"/>
+            <validator id="GeomValidators_Different" parameters="first_point,second_point,passed_point"/>
+            <validator id="SketchPlugin_DifferentPointReference" parameters="first_point_ref,second_point_ref"/>
           </box>
           <box id="by_major_axis_and_point"
                icon="icons/Sketch/ellipse_axes_32x32.png"
                                      tooltip="Passed point coordinates"
                                      accept_expressions="0"
                                      enable_value="enable_by_preferences"/>
+            <validator id="GeomValidators_Different" parameters="first_point_1,second_point_1,passed_point_1"/>
+            <validator id="SketchPlugin_DifferentPointReference" parameters="first_point_ref_1,second_point_ref_1"/>
           </box>
         </toolbox>
         <labelvalue id="major_radius"
                    default="false"
                    obligatory="0"
                    change_visual_attributes="true"/>
+        <validator id="GeomValidators_Different" parameters="center,major_axis_point"/>
+        <validator id="SketchPlugin_DifferentPointReference" parameters="center_ref,major_axis_point_ref"/>
+        <validator id="GeomValidators_Different" parameters="start_point,end_point"/>
+        <validator id="SketchPlugin_DifferentPointReference" parameters="start_point_ref,end_point_ref"/>
+      </feature>
+    </group>
+
+    <group id="Parametric curves">
+      <!-- SketchBSpline is a hidden feature. It is created inside SketchMacroBSpline. -->
+      <feature id="SketchBSpline"
+               title="B-spline"
+               tooltip="Create B-spline curve"
+               icon="icons/Sketch/bspline.png"
+               helpfile="bsplineFeature.html"
+               internal="1">
+        <bspline-panel id="poles"
+                       weights="weights"
+                       title="Poles and weights"
+                       tooltip="B-spline poles and weights"
+                       enable_value="enable_by_preferences">
+          <validator id="SketchPlugin_BSplineValidator"/>
+        </bspline-panel>
+        <boolvalue id="Auxiliary"
+                   label="Auxiliary"
+                   default="false"
+                   tooltip="Construction element"
+                   obligatory="0"
+                   change_visual_attributes="true"/>
+      </feature>
+
+      <!-- SketchBSplinePeriodic is a hidden feature. It is created inside SketchMacroBSplinePeriodic. -->
+      <feature id="SketchBSplinePeriodic"
+               title="Periodic B-spline"
+               tooltip="Create periodic B-spline curve"
+               icon="icons/Sketch/bspline_p.png"
+               helpfile="bsplineFeature.html"
+               internal="1">
+        <bspline-panel id="poles"
+                       weights="weights"
+                       title="Poles and weights"
+                       tooltip="B-spline poles and weights"
+                       enable_value="enable_by_preferences">
+          <validator id="SketchPlugin_BSplineValidator"/>
+        </bspline-panel>
+        <boolvalue id="Auxiliary"
+                   label="Auxiliary"
+                   default="false"
+                   tooltip="Construction element"
+                   obligatory="0"
+                   change_visual_attributes="true"/>
+      </feature>
+
+      <!-- SketchMacroBSpline -->
+      <feature id="SketchMacroBSpline"
+               title="B-spline"
+               tooltip="Create B-spline curve"
+               icon="icons/Sketch/bspline.png"
+               helpfile="bsplineFeature.html">
+        <sketch-bspline_selector id="poles"
+                                 weights="weights"
+                                 reference_attribute="poles_ref"
+                                 title="Poles and weights"
+                                 tooltip="B-spline poles and weights"
+                                 enable_value="enable_by_preferences">
+          <validator id="SketchPlugin_BSplineValidator"/>
+        </sketch-bspline_selector>
+        <boolvalue id="need_control_poly"
+                   label="Create control polygon"
+                   default="true"
+                   tooltip="Specify if the control polygon should be created"/>
+        <boolvalue id="Auxiliary"
+                   label="Auxiliary"
+                   default="false"
+                   tooltip="Construction element"
+                   obligatory="0"
+                   change_visual_attributes="true"/>
+      </feature>
+
+      <!-- SketchMacroBSplinePeriodic -->
+      <feature id="SketchMacroBSplinePeriodic"
+               title="Periodic B-spline"
+               tooltip="Create periodic B-spline curve"
+               icon="icons/Sketch/bspline_p.png"
+               helpfile="bsplineFeature.html">
+        <sketch-bspline_selector id="poles"
+                                 weights="weights"
+                                 reference_attribute="poles_ref"
+                                 title="Poles and weights"
+                                 tooltip="B-spline poles and weights"
+                                 enable_value="enable_by_preferences">
+          <validator id="SketchPlugin_BSplineValidator"/>
+        </sketch-bspline_selector>
+        <boolvalue id="need_control_poly"
+                   label="Create control polygon"
+                   default="true"
+                   tooltip="Specify if the control polygon should be created"/>
+        <boolvalue id="Auxiliary"
+                   label="Auxiliary"
+                   default="false"
+                   tooltip="Construction element"
+                   obligatory="0"
+                   change_visual_attributes="true"/>
       </feature>
     </group>
 
           buttons_dir="horizontal"
           label="Angle type"
           tooltip="Type of angle"
-          string_list="Direct Complementary Additional"
+          string_list="Direct Supplementary Additional"
           icons_list="icons/Sketch/angle_direct.png icons/Sketch/angle_complementary.png icons/Sketch/angle_backward.png"
           default="0"
           />
index fab1f0356d3f363e00fff20140a125395f3172e9..61a812a8b2dbc88e7ce32178e012b01d394a638a 100644 (file)
@@ -27,10 +27,13 @@ SET(PLANEGCSSOLVER_HEADERS
     PlaneGCSSolver_EdgeWrapper.h
     PlaneGCSSolver_EntityWrapper.h
     PlaneGCSSolver_PointWrapper.h
+    PlaneGCSSolver_PointArrayWrapper.h
     PlaneGCSSolver_ScalarWrapper.h
+    PlaneGCSSolver_ScalarArrayWrapper.h
     PlaneGCSSolver_AngleWrapper.h
     PlaneGCSSolver_BooleanWrapper.h
     PlaneGCSSolver_Tools.h
+    PlaneGCSSolver_GeoExtensions.h
 )
 
 SET(PLANEGCSSOLVER_SOURCES
@@ -39,10 +42,13 @@ SET(PLANEGCSSOLVER_SOURCES
     PlaneGCSSolver_ConstraintWrapper.cpp
     PlaneGCSSolver_EdgeWrapper.cpp
     PlaneGCSSolver_PointWrapper.cpp
+    PlaneGCSSolver_PointArrayWrapper.cpp
     PlaneGCSSolver_ScalarWrapper.cpp
+    PlaneGCSSolver_ScalarArrayWrapper.cpp
     PlaneGCSSolver_AngleWrapper.cpp
     PlaneGCSSolver_BooleanWrapper.cpp
     PlaneGCSSolver_Tools.cpp
+    PlaneGCSSolver_GeoExtensions.cpp
 )
 
 SET(PLANEGCSSOLVER_BUILDER_HEADERS
index ca463c53d70f063745d9f062bf7c250cc6810d24..439a18f17be450dc0d5ee3f1b08f3261ded27311 100644 (file)
 
 #include <PlaneGCSSolver_AngleWrapper.h>
 #include <PlaneGCSSolver_AttributeBuilder.h>
+#include <PlaneGCSSolver_PointArrayWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_ScalarArrayWrapper.h>
 #include <PlaneGCSSolver_BooleanWrapper.h>
+#include <PlaneGCSSolver_Tools.h>
 
+#include <GeomAPI_Pnt2d.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 #include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
 #include <SketchPlugin_ConstraintAngle.h>
 #include <SketchPlugin_MultiRotation.h>
 
@@ -58,9 +67,17 @@ static EntityWrapperPtr createBoolean(const AttributePtr& theAttribute)
 static EntityWrapperPtr createScalar(const AttributePtr&     theAttribute,
                                      PlaneGCSSolver_Storage* theStorage)
 {
-  AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
-  if (!aScalar)
-    return EntityWrapperPtr();
+  double aValue = 0.0;
+  AttributeDoublePtr aDouble = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
+  if (aDouble)
+    aValue = aDouble->isInitialized() ? aDouble->value() : 0.0;
+  else {
+    AttributeIntegerPtr anInt = std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(theAttribute);
+    if (anInt)
+      aValue = anInt->isInitialized() ? anInt->value() : 0.0;
+    else
+      return EntityWrapperPtr();
+  }
 
   ScalarWrapperPtr aWrapper;
   // following attributes should be converted from degrees to radians
@@ -72,14 +89,66 @@ static EntityWrapperPtr createScalar(const AttributePtr&     theAttribute,
      (theAttribute->id() == SketchPlugin_MultiRotation::ANGLE_ID() &&
       anOwner->getKind() == SketchPlugin_MultiRotation::ID()))
     aWrapper = ScalarWrapperPtr(new PlaneGCSSolver_AngleWrapper(createParameter(theStorage)));
+  else if ((anOwner->getKind() == SketchPlugin_BSpline::ID() ||
+            anOwner->getKind() == SketchPlugin_BSplinePeriodic::ID()) &&
+           theAttribute->id() == SketchPlugin_BSplineBase::DEGREE_ID())
+    // Degree of B-spline is not processed by the solver
+    aWrapper = ScalarWrapperPtr(new PlaneGCSSolver_ScalarWrapper(createParameter(nullptr)));
   else
     aWrapper = ScalarWrapperPtr(new PlaneGCSSolver_ScalarWrapper(createParameter(theStorage)));
 
-  if (aScalar->isInitialized())
-    aWrapper->setValue(aScalar->value());
+  aWrapper->setValue(aValue);
   return aWrapper;
 }
 
+template <typename TYPE>
+static bool nonSolverAttribute(const FeaturePtr theOwner, const std::string& theAttrId)
+{
+  return theOwner->getKind() == TYPE::ID() && (theAttrId == TYPE::WEIGHTS_ID()
+      || theAttrId == TYPE::KNOTS_ID() || theAttrId == TYPE::MULTS_ID());
+}
+
+static EntityWrapperPtr createScalarArray(const AttributePtr&     theAttribute,
+                                          PlaneGCSSolver_Storage* theStorage)
+{
+  PlaneGCSSolver_Tools::AttributeArray anArray(theAttribute);
+
+  if (!anArray.isInitialized())
+    return EntityWrapperPtr();
+
+  PlaneGCSSolver_Storage* aStorage = theStorage;
+  // Weights, knots and multiplicities of B-spline curve are not processed by the solver
+  FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
+  if (nonSolverAttribute<SketchPlugin_BSpline>(anOwner, theAttribute->id()) ||
+      nonSolverAttribute<SketchPlugin_BSplinePeriodic>(anOwner, theAttribute->id()))
+    aStorage = 0;
+
+  int aSize = anArray.size();
+  GCS::VEC_pD aParameters;
+  aParameters.reserve(aSize);
+  for (int index = 0; index < aSize; ++index) {
+    double* aParam = createParameter(aStorage);
+    *aParam = anArray.value(index);
+    aParameters.push_back(aParam);
+  }
+
+  return EntityWrapperPtr(new PlaneGCSSolver_ScalarArrayWrapper(aParameters));
+}
+
+static PointWrapperPtr createPoint(const GeomPnt2dPtr& thePoint, PlaneGCSSolver_Storage* theStorage)
+{
+  GCSPointPtr aNewPoint(new GCS::Point);
+
+  aNewPoint->x = createParameter(theStorage);
+  aNewPoint->y = createParameter(theStorage);
+  if (thePoint) {
+    *(aNewPoint->x) = thePoint->x();
+    *(aNewPoint->y) = thePoint->y();
+  }
+
+  return PointWrapperPtr(new PlaneGCSSolver_PointWrapper(aNewPoint));
+}
+
 static EntityWrapperPtr createPoint(const AttributePtr&     theAttribute,
                                     PlaneGCSSolver_Storage* theStorage)
 {
@@ -88,16 +157,29 @@ static EntityWrapperPtr createPoint(const AttributePtr&     theAttribute,
   if (!aPoint2D)
     return EntityWrapperPtr();
 
-  GCSPointPtr aNewPoint(new GCS::Point);
+  GeomPnt2dPtr aPnt;
+  if (aPoint2D->isInitialized())
+    aPnt = aPoint2D->pnt();
 
-  aNewPoint->x = createParameter(theStorage);
-  aNewPoint->y = createParameter(theStorage);
-  if (aPoint2D->isInitialized()) {
-    *(aNewPoint->x) = aPoint2D->x();
-    *(aNewPoint->y) = aPoint2D->y();
-  }
+  return createPoint(aPnt, theStorage);
+}
+
+static EntityWrapperPtr createPointArray(const AttributePtr& theAttribute,
+                                         PlaneGCSSolver_Storage* theStorage)
+{
+  std::shared_ptr<GeomDataAPI_Point2DArray> aPointArray =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(theAttribute);
+  if (!aPointArray)
+    return EntityWrapperPtr();
+
+  int aSize = aPointArray->size();
 
-  return EntityWrapperPtr(new PlaneGCSSolver_PointWrapper(aNewPoint));
+  std::vector<PointWrapperPtr> aPointWrappers;
+  aPointWrappers.reserve(aSize);
+  for (int index = 0; index < aSize; ++index)
+    aPointWrappers.push_back(createPoint(aPointArray->pnt(index), theStorage));
+
+  return EntityWrapperPtr(new PlaneGCSSolver_PointArrayWrapper(aPointWrappers));
 }
 
 EntityWrapperPtr PlaneGCSSolver_AttributeBuilder::createAttribute(
@@ -112,7 +194,85 @@ EntityWrapperPtr PlaneGCSSolver_AttributeBuilder::createAttribute(
     aResult = createScalar(theAttribute, myStorage);
   if (!aResult)
     aResult = createBoolean(theAttribute);
+  if (!aResult)
+    aResult = createPointArray(theAttribute, myStorage);
+  if (!aResult)
+    aResult = createScalarArray(theAttribute, myStorage);
   if (aResult && !myStorage)
     aResult->setExternal(true);
   return aResult;
 }
+
+bool PlaneGCSSolver_AttributeBuilder::updateAttribute(
+    AttributePtr theAttribute,
+    EntityWrapperPtr theEntity)
+{
+  bool isUpdated = false;
+  GCS::SET_pD aParamsToRemove;
+  // rebuild array if its size is changed
+  if (theEntity->type() == ENTITY_POINT_ARRAY) {
+    std::shared_ptr<PlaneGCSSolver_PointArrayWrapper> aWrapper =
+        std::dynamic_pointer_cast<PlaneGCSSolver_PointArrayWrapper>(theEntity);
+    std::shared_ptr<GeomDataAPI_Point2DArray> anAttribute =
+        std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(theAttribute);
+
+    std::vector<PointWrapperPtr> aPointsArray = aWrapper->array();
+    std::vector<PointWrapperPtr>::iterator aPos = aPointsArray.begin();
+    if (aWrapper->size() != anAttribute->size()) {
+      while (anAttribute->size() > (int)aPointsArray.size()) {
+        // add points to the middle of array
+        GeomPnt2dPtr aValue;
+        for (; aPos != aPointsArray.end(); ++aPos) {
+          aValue = anAttribute->pnt(aPos - aPointsArray.begin());
+          if (aValue->distance(PlaneGCSSolver_Tools::point(*aPos)) > tolerance)
+            break;
+        }
+        int aShift = aPos - aPointsArray.begin();
+        aPointsArray.insert(aPos, createPoint(aValue, myStorage));
+        aPos = aPointsArray.begin() + aShift;
+      }
+
+      while (anAttribute->size() < (int)aPointsArray.size()) {
+        // remove middle points
+        std::vector<PointWrapperPtr>::iterator anIt = --aPointsArray.end();
+        GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(*anIt);
+        aParamsToRemove.insert(aParams.begin(), aParams.end());
+        aPointsArray.erase(anIt);
+      }
+
+      aWrapper->setArray(aPointsArray);
+    }
+    else {
+      // update coordinates of points
+      for (int anIndex = 0; aPos != aPointsArray.end(); ++aPos, ++anIndex) {
+        const GCSPointPtr& aGCSPoint = (*aPos)->point();
+        GeomPnt2dPtr aCoord = anAttribute->pnt(anIndex);
+        *aGCSPoint->x = aCoord->x();
+        *aGCSPoint->y = aCoord->y();
+      }
+    }
+  }
+  else if (theEntity->type() == ENTITY_SCALAR_ARRAY) {
+    std::shared_ptr<PlaneGCSSolver_ScalarArrayWrapper> aWrapper =
+        std::dynamic_pointer_cast<PlaneGCSSolver_ScalarArrayWrapper>(theEntity);
+    if (aWrapper->size() != PlaneGCSSolver_Tools::AttributeArray(theAttribute).size()) {
+      aParamsToRemove = PlaneGCSSolver_Tools::parameters(aWrapper);
+      std::shared_ptr<PlaneGCSSolver_ScalarArrayWrapper> aNewArray =
+          std::dynamic_pointer_cast<PlaneGCSSolver_ScalarArrayWrapper>(
+          createScalarArray(theAttribute, myStorage));
+      aWrapper->setArray(aNewArray->array());
+      isUpdated = true;
+    }
+  }
+
+  if (!aParamsToRemove.empty()) {
+    if (myStorage)
+      myStorage->removeParameters(aParamsToRemove);
+    else {
+      std::for_each(aParamsToRemove.begin(), aParamsToRemove.end(),
+                    [](double* theParam) { delete theParam; });
+    }
+  }
+
+  return isUpdated || theEntity->update(theAttribute);
+}
index 7d21983b30337becddb0b02252bacd8b35bce714..adc0e5720212174335716a65abd90254b7ff250e 100644 (file)
@@ -38,6 +38,10 @@ public:
   /// \return Created wrapper of the attribute applicable for specific solver
   virtual EntityWrapperPtr createAttribute(AttributePtr theAttribute);
 
+  /// \brief Update entity by the attribute values.
+  /// \return \c true if any value is updated.
+  virtual bool updateAttribute(AttributePtr theAttribute, EntityWrapperPtr theEntity);
+
   /// \brief Blank. To be defined in derived class.
   virtual EntityWrapperPtr createFeature(FeaturePtr)
   { return EntityWrapperPtr(); }
index 225c4344f73e98ce386bafce57f52bed100774a6..be2e0bf81113065e1e394a0bb7f910db40fe7402 100644 (file)
 
 #include <PlaneGCSSolver_BooleanWrapper.h>
 
+#include <ModelAPI_AttributeBoolean.h>
+
 PlaneGCSSolver_BooleanWrapper::PlaneGCSSolver_BooleanWrapper(bool theParam)
   : myValue(theParam)
 {
 }
+
+bool PlaneGCSSolver_BooleanWrapper::update(AttributePtr theAttribute)
+{
+  bool isUpdated = false;
+  AttributeBooleanPtr aBoolean =
+      std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
+  if (aBoolean) {
+    isUpdated = value() != aBoolean->value();
+    setValue(aBoolean->value());
+  }
+  return isUpdated;
+}
index 3bd1d8d2ed7ed5455b909cbda6b7bee6e9692db2..667eb083dea0ae0385daa6272d71fbe12cf68a0a 100644 (file)
@@ -42,6 +42,11 @@ public:
   virtual SketchSolver_EntityType type() const
   { return ENTITY_BOOLEAN; }
 
+protected:
+  /// \brief Update entity by the values of theAttribute
+  /// \return \c true if any value of attribute is not equal to the stored in the entity
+  virtual bool update(std::shared_ptr<ModelAPI_Attribute> theAttribute);
+
 protected:
   bool myValue;
 };
index b99cc7c3b1bd00160a3d9cb2ea782555d15ca295..ab6ea78943dbf91d7076c0f795a2577e7744eef1 100644 (file)
@@ -23,6 +23,8 @@
 #include <PlaneGCSSolver_Defs.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
 
+#include <list>
+
 /**
  *  Wrapper providing operations with PlaneGCS constraints.
  */
index 0f46e7544d73920b8f3349618b3f1a6dc7c4d982..eea27bf06faccc7087337d4481a3a7b081ee49e3 100644 (file)
@@ -46,13 +46,16 @@ enum SketchSolver_EntityType {
   ENTITY_UNKNOWN = 0,
   ENTITY_BOOLEAN,
   ENTITY_SCALAR,
+  ENTITY_SCALAR_ARRAY,
   ENTITY_ANGLE,
   ENTITY_POINT,
+  ENTITY_POINT_ARRAY,
   ENTITY_LINE,
   ENTITY_CIRCLE,
   ENTITY_ARC,
   ENTITY_ELLIPSE,
-  ENTITY_ELLIPTIC_ARC
+  ENTITY_ELLIPTIC_ARC,
+  ENTITY_BSPLINE
 };
 
 /// Types of constraints
index f9683203880051e28acbc8a99d0e71ef8d4cf014..669aece33073ca8e85916c47b7fe8c0f875fcd61 100644 (file)
 #include <PlaneGCSSolver_EdgeWrapper.h>
 #include <cmath>
 
-static bool isLine(const GCSCurvePtr& theEntity)
+template <typename TYPE>
+static bool isCurve(const GCSCurvePtr& theEntity)
 {
-  return std::dynamic_pointer_cast<GCS::Line>(theEntity).get();
-}
-
-static bool isCircle(const GCSCurvePtr& theEntity)
-{
-  return std::dynamic_pointer_cast<GCS::Circle>(theEntity).get();
-}
-
-static bool isArc(const GCSCurvePtr& theEntity)
-{
-  return std::dynamic_pointer_cast<GCS::Arc>(theEntity).get();
-}
-
-static bool isEllipse(const GCSCurvePtr& theEntity)
-{
-  return std::dynamic_pointer_cast<GCS::Ellipse>(theEntity).get();
-}
-
-static bool isEllipticArc(const GCSCurvePtr& theEntity)
-{
-  return std::dynamic_pointer_cast<GCS::ArcOfEllipse>(theEntity).get();
+  return std::dynamic_pointer_cast<TYPE>(theEntity).get();
 }
 
 
 PlaneGCSSolver_EdgeWrapper::PlaneGCSSolver_EdgeWrapper(const GCSCurvePtr theEntity)
   : myEntity(theEntity)
 {
-  if (isLine(myEntity))
+  if (isCurve<GCS::Line>(myEntity))
     myType = ENTITY_LINE;
-  else if (isArc(myEntity))
+  else if (isCurve<GCS::Arc>(myEntity))
     myType = ENTITY_ARC;
-  else if (isCircle(myEntity))
+  else if (isCurve<GCS::Circle>(myEntity))
     myType = ENTITY_CIRCLE;
-  else if (isEllipticArc(myEntity))
+  else if (isCurve<GCS::ArcOfEllipse>(myEntity))
     myType = ENTITY_ELLIPTIC_ARC;
-  else if (isEllipse(myEntity))
+  else if (isCurve<GCS::Ellipse>(myEntity))
     myType = ENTITY_ELLIPSE;
+  else if (isCurve<GCS::BSpline>(myEntity))
+    myType = ENTITY_BSPLINE;
 }
 
 static double squareDistance(const GCS::Point& theP1, const GCS::Point& theP2)
index 5eceaf63c5c20f1d63906b4584d4ee401b24eabc..bc86246e588e66eb2e1137236427ac7648c670ee 100644 (file)
 
 #include <PlaneGCSSolver_Defs.h>
 
-#include <ModelAPI_Attribute.h>
-#include <ModelAPI_Feature.h>
-
-#include <list>
+#include <map>
 #include <memory>
 
+class ModelAPI_Attribute;
+
 class PlaneGCSSolver_EntityWrapper;
 typedef std::shared_ptr<PlaneGCSSolver_EntityWrapper> EntityWrapperPtr;
 
@@ -55,6 +54,14 @@ public:
   const std::map<std::string, EntityWrapperPtr>& additionalAttributes() const
   { return myAdditionalAttributes; }
 
+protected:
+  /// \brief Update entity by the values of theAttribute
+  /// \return \c true if any value of attribute is not equal to the stored in the entity
+  virtual bool update(std::shared_ptr<ModelAPI_Attribute> theAttribute)
+  { return false; }
+
+  friend class PlaneGCSSolver_AttributeBuilder;
+
 private:
   bool myExternal;
   std::map<std::string, EntityWrapperPtr> myAdditionalAttributes;
index 400319aed4bd9804a46ec5c2c118da1d8473c9d7..e797abad2342bb7f6207dc48d1e65bfcb2a8ef3c 100644 (file)
 //
 
 #include <PlaneGCSSolver_FeatureBuilder.h>
+#include <PlaneGCSSolver_BooleanWrapper.h>
 #include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_GeoExtensions.h>
 #include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_PointArrayWrapper.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
-#include <PlaneGCSSolver_BooleanWrapper.h>
+#include <PlaneGCSSolver_ScalarArrayWrapper.h>
 #include <PlaneGCSSolver_Tools.h>
 
 #include <SketchPlugin_Arc.h>
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_Ellipse.h>
 #include <SketchPlugin_EllipticArc.h>
@@ -36,8 +41,6 @@
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_XY.h>
 
-static bool isAttributeApplicable(const std::string& theAttrName,
-                                  const std::string& theOwnerName);
 
 static EntityWrapperPtr createLine(const AttributeEntityMap& theAttributes);
 static EntityWrapperPtr createCircle(const AttributeEntityMap& theAttributes);
@@ -46,6 +49,8 @@ static EntityWrapperPtr createArc(const AttributeEntityMap&    theAttributes,
 static EntityWrapperPtr createEllipse(const AttributeEntityMap& theAttributes);
 static EntityWrapperPtr createEllipticArc(const AttributeEntityMap& theAttributes,
                                           PlaneGCSSolver_Storage*   theStorage);
+template <typename TYPE>
+static EntityWrapperPtr createBSpline(const AttributeEntityMap& theAttributes);
 
 
 PlaneGCSSolver_FeatureBuilder::PlaneGCSSolver_FeatureBuilder(
@@ -64,7 +69,7 @@ EntityWrapperPtr PlaneGCSSolver_FeatureBuilder::createAttribute(
 {
   FeaturePtr anOwner = ModelAPI_Feature::feature(theAttribute->owner());
   EntityWrapperPtr anAttr;
-  if (isAttributeApplicable(theAttribute->id(), anOwner->getKind()))
+  if (PlaneGCSSolver_Tools::isAttributeApplicable(theAttribute->id(), anOwner->getKind()))
     anAttr = PlaneGCSSolver_AttributeBuilder::createAttribute(theAttribute);
   if (anAttr)
     myAttributes[theAttribute] = anAttr;
@@ -102,6 +107,11 @@ EntityWrapperPtr PlaneGCSSolver_FeatureBuilder::createFeature(FeaturePtr theFeat
   // Arc of ellipse
   else if (aFeatureKind == SketchPlugin_EllipticArc::ID())
     aResult = createEllipticArc(myAttributes, myStorage);
+  // B-spline curve
+  else if (aFeatureKind == SketchPlugin_BSpline::ID())
+    aResult = createBSpline<SketchPlugin_BSpline>(myAttributes);
+  else if (aFeatureKind == SketchPlugin_BSplinePeriodic::ID())
+    aResult = createBSpline<SketchPlugin_BSplinePeriodic>(myAttributes);
   // Point (it has low probability to be an attribute of constraint, so it is checked at the end)
   else if (aFeatureKind == SketchPlugin_Point::ID() ||
            aFeatureKind == SketchPlugin_IntersectionPoint::ID()) {
@@ -288,48 +298,57 @@ EntityWrapperPtr createEllipticArc(const AttributeEntityMap& theAttributes,
   return anEllipseWrapper;
 }
 
-bool isAttributeApplicable(const std::string& theAttrName, const std::string& theOwnerName)
+template <typename TYPE>
+EntityWrapperPtr createBSpline(const AttributeEntityMap& theAttributes)
 {
-  if (theOwnerName == SketchPlugin_Arc::ID()) {
-    return theAttrName == SketchPlugin_Arc::CENTER_ID() ||
-           theAttrName == SketchPlugin_Arc::START_ID() ||
-           theAttrName == SketchPlugin_Arc::END_ID() ||
-           theAttrName == SketchPlugin_Arc::REVERSED_ID();
-  }
-  else if (theOwnerName == SketchPlugin_Circle::ID()) {
-    return theAttrName == SketchPlugin_Circle::CENTER_ID() ||
-           theAttrName == SketchPlugin_Circle::RADIUS_ID();
-  }
-  else if (theOwnerName == SketchPlugin_Line::ID()) {
-    return theAttrName == SketchPlugin_Line::START_ID() ||
-           theAttrName == SketchPlugin_Line::END_ID();
-  }
-  else if (theOwnerName == SketchPlugin_Ellipse::ID()) {
-    return theAttrName == SketchPlugin_Ellipse::CENTER_ID() ||
-           theAttrName == SketchPlugin_Ellipse::FIRST_FOCUS_ID() ||
-           theAttrName == SketchPlugin_Ellipse::SECOND_FOCUS_ID() ||
-           theAttrName == SketchPlugin_Ellipse::MAJOR_AXIS_START_ID() ||
-           theAttrName == SketchPlugin_Ellipse::MAJOR_AXIS_END_ID() ||
-           theAttrName == SketchPlugin_Ellipse::MINOR_AXIS_START_ID() ||
-           theAttrName == SketchPlugin_Ellipse::MINOR_AXIS_END_ID() ||
-           theAttrName == SketchPlugin_Ellipse::MAJOR_RADIUS_ID() ||
-           theAttrName == SketchPlugin_Ellipse::MINOR_RADIUS_ID();
-  }
-  else if (theOwnerName == SketchPlugin_EllipticArc::ID()) {
-    return theAttrName == SketchPlugin_EllipticArc::CENTER_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::FIRST_FOCUS_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::SECOND_FOCUS_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::MAJOR_AXIS_START_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::MAJOR_AXIS_END_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::MINOR_AXIS_START_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::MINOR_AXIS_END_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::MAJOR_RADIUS_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::MINOR_RADIUS_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::START_POINT_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::END_POINT_ID() ||
-           theAttrName == SketchPlugin_EllipticArc::REVERSED_ID();
+  std::shared_ptr<GCS::BSplineImpl> aNewSpline(new GCS::BSplineImpl);
+
+  aNewSpline->degree = 3;
+  aNewSpline->periodic = std::is_same<TYPE, SketchPlugin_BSplinePeriodic>();
+
+  std::map<std::string, EntityWrapperPtr> anAdditionalAttributes;
+
+  AttributeEntityMap::const_iterator anIt = theAttributes.begin();
+  for (; anIt != theAttributes.end(); ++anIt) {
+    const std::string& anAttrID = anIt->first->id();
+    if (anAttrID == TYPE::POLES_ID()) {
+      PointArrayWrapperPtr anArray =
+          std::dynamic_pointer_cast<PlaneGCSSolver_PointArrayWrapper>(anIt->second);
+
+      int aSize = anArray->size();
+      aNewSpline->poles.reserve(aSize);
+      for (int anIndex = 0; anIndex < aSize; ++anIndex)
+        aNewSpline->poles.push_back(*anArray->value(anIndex)->point());
+
+      aNewSpline->start = aNewSpline->poles.front();
+      aNewSpline->end = aNewSpline->poles.back();
+    }
+    else if (anAttrID == TYPE::DEGREE_ID()) {
+      ScalarWrapperPtr aScalar =
+          std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(anIt->second);
+      aNewSpline->degree = (int)aScalar->value();
+    }
+    else if (anAttrID == SketchPlugin_BSpline::START_ID() ||
+             anAttrID == SketchPlugin_BSpline::END_ID()) {
+      anAdditionalAttributes[anAttrID] = anIt->second;
+    }
+    else {
+      ScalarArrayWrapperPtr anArray =
+          std::dynamic_pointer_cast<PlaneGCSSolver_ScalarArrayWrapper>(anIt->second);
+      if (anAttrID == TYPE::WEIGHTS_ID())
+        aNewSpline->weights = anArray->array();
+      else if (anAttrID == TYPE::KNOTS_ID())
+        aNewSpline->knots = anArray->array();
+      else if (anAttrID == TYPE::MULTS_ID()) {
+        const GCS::VEC_pD& aValues = anArray->array();
+        aNewSpline->mult.reserve(aValues.size());
+        for (GCS::VEC_pD::const_iterator anIt = aValues.begin(); anIt != aValues.end(); ++anIt)
+          aNewSpline->mult.push_back((int)(**anIt));
+      }
+    }
   }
 
-  // suppose that all remaining features are points
-  return theAttrName == SketchPlugin_Point::COORD_ID();
+  EdgeWrapperPtr aWrapper(new PlaneGCSSolver_EdgeWrapper(aNewSpline));
+  aWrapper->setAdditionalAttributes(anAdditionalAttributes);
+  return aWrapper;
 }
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.cpp
new file mode 100644 (file)
index 0000000..480dbd5
--- /dev/null
@@ -0,0 +1,123 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <PlaneGCSSolver_GeoExtensions.h>
+#include <PlaneGCSSolver_Tools.h>
+
+#include <GeomAPI_BSpline2d.h>
+#include <GeomAPI_Pnt2d.h>
+#include <GeomAPI_XY.h>
+
+#include <cmath>
+
+namespace GCS
+{
+
+DeriVector2 BSplineImpl::Value(double u, double du, double* derivparam)
+{
+  if (!isCacheValid())
+    rebuildCache();
+
+  std::shared_ptr<GeomAPI_Pnt2d> aValue;
+  std::shared_ptr<GeomAPI_XY> aDeriv;
+  myCurve->D1(u, aValue, aDeriv);
+
+  return DeriVector2(aValue->x(), aValue->y(), aDeriv->x() * du, aDeriv->y() * du);
+}
+
+DeriVector2 BSplineImpl::CalculateNormal(Point &p, double* derivparam)
+{
+  if (!isCacheValid())
+    rebuildCache();
+
+  double u = 0.0;
+  if (!myCurve->parameter(GeomPnt2dPtr(new GeomAPI_Pnt2d(*p.x, *p.y)), 1e100, u)) {
+    // Sometimes OCCT's Extrema algorithm cannot find the parameter on B-spline curve
+    // (usually, if the point is near the curve extremity).
+    // Workaround: compute distance to each boundary point
+    double aDistPS = PlaneGCSSolver_Tools::distance(p, poles.front());
+    double aDistPE = PlaneGCSSolver_Tools::distance(p, poles.back());
+    static const double THE_TOLERANCE = 1.e-6;
+    if (aDistPS < aDistPE && aDistPS < THE_TOLERANCE)
+      u = *knots.front();
+    else if (aDistPE < aDistPS && aDistPE < THE_TOLERANCE)
+      u = *knots.back();
+    else
+      return DeriVector2();
+  }
+
+  std::shared_ptr<GeomAPI_Pnt2d> aValue;
+  std::shared_ptr<GeomAPI_XY> aDeriv;
+  myCurve->D1(u, aValue, aDeriv);
+
+  DeriVector2 norm(aDeriv->x(), aDeriv->y(), 0.0, 0.0);
+  return norm.rotate90ccw();
+}
+
+BSplineImpl* BSplineImpl::Copy()
+{
+  return new BSplineImpl(*this);
+}
+
+
+bool BSplineImpl::isCacheValid() const
+{
+  // curve has to be initialized
+  bool isValid = myCurve.get() && !myCurve->isNull();
+
+  static const double THE_TOLERANCE = 1.e-7;
+  // compare poles
+  isValid = isValid && poles.size() == myCachedPoles.size();
+  std::list<GeomPnt2dPtr>::const_iterator aCachePIt = myCachedPoles.begin();
+  GCS::VEC_P::const_iterator aPolesIt = poles.begin();
+  for (; isValid && aPolesIt != poles.end(); ++aPolesIt, ++aCachePIt) {
+    isValid = isValid && fabs((*aCachePIt)->x() - *aPolesIt->x) < THE_TOLERANCE
+                      && fabs((*aCachePIt)->y() - *aPolesIt->y) < THE_TOLERANCE;
+  }
+
+  // compare weights
+  isValid = isValid && weights.size() == myCachedWeights.size();
+  std::list<double>::const_iterator aCacheWIt = myCachedWeights.begin();
+  GCS::VEC_pD::const_iterator aWeightsIt = weights.begin();
+  for (; isValid && aWeightsIt != weights.end(); ++aWeightsIt, ++aCacheWIt)
+    isValid = isValid && fabs(*aCacheWIt - **aWeightsIt) < THE_TOLERANCE;
+
+  return isValid;
+}
+
+void BSplineImpl::rebuildCache()
+{
+  myCachedPoles.clear();
+  myCachedWeights.clear();
+  myCachedKnots.clear();
+  myCachedMultiplicities.clear();
+
+  for (GCS::VEC_P::iterator anIt = poles.begin(); anIt != poles.end(); ++anIt)
+    myCachedPoles.push_back(GeomPnt2dPtr(new GeomAPI_Pnt2d(*anIt->x, *anIt->y)));
+  for (GCS::VEC_pD::iterator anIt = weights.begin(); anIt != weights.end(); ++anIt)
+    myCachedWeights.push_back(**anIt);
+  for (GCS::VEC_pD::iterator anIt = knots.begin(); anIt != knots.end(); ++anIt)
+    myCachedKnots.push_back(**anIt);
+  myCachedMultiplicities.assign(mult.begin(), mult.end());
+
+  myCurve.reset(new GeomAPI_BSpline2d(degree, myCachedPoles, myCachedWeights,
+                                      myCachedKnots, myCachedMultiplicities, periodic));
+}
+
+} // namespace GCS
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_GeoExtensions.h
new file mode 100644 (file)
index 0000000..c83e1a9
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef PlaneGCSSolver_GeomExtensions_H_
+#define PlaneGCSSolver_GeomExtensions_H_
+
+#include <PlaneGCSSolver_Defs.h>
+
+#include <list>
+#include <memory>
+
+class GeomAPI_BSpline2d;
+class GeomAPI_Pnt2d;
+
+namespace GCS {
+  /// \brife SHAPER's implementation of B-spline curves in PlaneGCS solver
+  class BSplineImpl : public BSpline
+  {
+  public:
+    virtual DeriVector2 Value(double u, double du, double* derivparam = 0);
+    virtual DeriVector2 CalculateNormal(Point &p, double* derivparam = 0);
+
+    virtual BSplineImpl* Copy();
+
+  private:
+    /// Verify the cached curve satisfies to the parameters
+    bool isCacheValid() const;
+    /// Poles or weights are changed, cache curve has to be rebuilt
+    void rebuildCache();
+
+  private:
+    std::shared_ptr<GeomAPI_BSpline2d> myCurve; /// cached B-spline curve
+    std::list<std::shared_ptr<GeomAPI_Pnt2d> > myCachedPoles; /// cached B-spline poles
+    std::list<double> myCachedWeights; /// cached B-spline weights
+    std::list<double> myCachedKnots; /// cached B-spline knots
+    std::list<int> myCachedMultiplicities; /// cached B-spline multiplicities
+  };
+}
+
+#endif
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.cpp
new file mode 100644 (file)
index 0000000..a409fb4
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <PlaneGCSSolver_PointArrayWrapper.h>
+#include <PlaneGCSSolver_Tools.h>
+
+#include <GeomDataAPI_Point2DArray.h>
+
+#include <GeomAPI_Pnt2d.h>
+
+PlaneGCSSolver_PointArrayWrapper::PlaneGCSSolver_PointArrayWrapper(
+    const std::vector<PointWrapperPtr>& thePoints)
+  : myPoints(thePoints)
+{
+}
+
+bool PlaneGCSSolver_PointArrayWrapper::update(AttributePtr theAttribute)
+{
+  bool isUpdated = false;
+  std::shared_ptr<GeomDataAPI_Point2DArray> aPointArray =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(theAttribute);
+  if (aPointArray && aPointArray->size() == (int)myPoints.size()) {
+    std::vector<PointWrapperPtr>::iterator aPIt = myPoints.begin();
+    for (int anIndex = 0; aPIt != myPoints.end(); ++aPIt, ++anIndex) {
+      GeomPnt2dPtr aPnt = aPointArray->pnt(anIndex);
+
+      const GCSPointPtr& aGCSPoint = (*aPIt)->point();
+      isUpdated = PlaneGCSSolver_Tools::updateValue(aPnt->x(), *(aGCSPoint->x)) || isUpdated;
+      isUpdated = PlaneGCSSolver_Tools::updateValue(aPnt->y(), *(aGCSPoint->y)) || isUpdated;
+    }
+  }
+  return isUpdated;
+}
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_PointArrayWrapper.h
new file mode 100644 (file)
index 0000000..0524be2
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef PlaneGCSSolver_PointArrayWrapper_H_
+#define PlaneGCSSolver_PointArrayWrapper_H_
+
+#include <PlaneGCSSolver_Defs.h>
+#include <PlaneGCSSolver_PointWrapper.h>
+
+/**
+ *  Wrapper providing operations with arrays of PlaneGCS points.
+ */
+class PlaneGCSSolver_PointArrayWrapper : public PlaneGCSSolver_EntityWrapper
+{
+public:
+  PlaneGCSSolver_PointArrayWrapper(const std::vector<PointWrapperPtr>& thePoints);
+
+  /// \brief Return wrapper of PlaneGCS point
+  const PointWrapperPtr& value(const int theIndex) const
+  { return myPoints[theIndex]; }
+
+  /// \breif Size of array
+  int size() const { return (int)myPoints.size(); }
+
+  /// \brief Return array of points
+  const std::vector<PointWrapperPtr>& array() const { return myPoints; }
+  /// \breif Set points
+  void setArray(const std::vector<PointWrapperPtr>& thePoints) { myPoints = thePoints; }
+
+  /// \brief Return type of current entity
+  virtual SketchSolver_EntityType type() const
+  { return ENTITY_POINT_ARRAY; }
+
+protected:
+  /// \brief Update entity by the values of theAttribute
+  /// \return \c true if any value of attribute is not equal to the stored in the entity
+  virtual bool update(std::shared_ptr<ModelAPI_Attribute> theAttribute);
+
+private:
+  std::vector<PointWrapperPtr> myPoints;
+};
+
+typedef std::shared_ptr<PlaneGCSSolver_PointArrayWrapper> PointArrayWrapperPtr;
+
+#endif
index bfb662590ad96dd2bdfe647a1404b7518b5ff179..bda73b1977ef5ac62285794385701f4461ac83b6 100644 (file)
 //
 
 #include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_Tools.h>
+
+#include <GeomDataAPI_Point2D.h>
 
 PlaneGCSSolver_PointWrapper::PlaneGCSSolver_PointWrapper(const GCSPointPtr thePoint)
   : myPoint(thePoint)
 {
 }
+
+bool PlaneGCSSolver_PointWrapper::update(AttributePtr theAttribute)
+{
+  bool isUpdated = false;
+  std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
+  if (aPoint2D) {
+    isUpdated = PlaneGCSSolver_Tools::updateValue(aPoint2D->x(), *(myPoint->x)) || isUpdated;
+    isUpdated = PlaneGCSSolver_Tools::updateValue(aPoint2D->y(), *(myPoint->y)) || isUpdated;
+  }
+  return isUpdated;
+}
index 3cb05e124f04505181e834b2cbbf6fc29bd1716d..a50497523bc04a1f38aeff314fc0bfde437e4b08 100644 (file)
@@ -42,6 +42,11 @@ public:
   virtual SketchSolver_EntityType type() const
   { return ENTITY_POINT; }
 
+protected:
+  /// \brief Update entity by the values of theAttribute
+  /// \return \c true if any value of attribute is not equal to the stored in the entity
+  virtual bool update(std::shared_ptr<ModelAPI_Attribute> theAttribute);
+
 private:
   GCSPointPtr myPoint;
 };
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.cpp b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.cpp
new file mode 100644 (file)
index 0000000..9d56154
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <PlaneGCSSolver_ScalarArrayWrapper.h>
+#include <PlaneGCSSolver_Tools.h>
+
+PlaneGCSSolver_ScalarArrayWrapper::PlaneGCSSolver_ScalarArrayWrapper(const GCS::VEC_pD& theParam)
+  : myValue(theParam)
+{
+}
+
+bool PlaneGCSSolver_ScalarArrayWrapper::update(AttributePtr theAttribute)
+{
+  bool isUpdated = false;
+  PlaneGCSSolver_Tools::AttributeArray anArray(theAttribute);
+  if (anArray.isInitialized() && anArray.size() == (int)myValue.size()) {
+    for (int anIndex = 0; anIndex < anArray.size(); ++anIndex) {
+      isUpdated = PlaneGCSSolver_Tools::updateValue(anArray.value(anIndex), *(myValue[anIndex]))
+          || isUpdated;
+    }
+  }
+  return isUpdated;
+}
diff --git a/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.h b/src/SketchSolver/PlaneGCSSolver/PlaneGCSSolver_ScalarArrayWrapper.h
new file mode 100644 (file)
index 0000000..6b0992e
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (C) 2019-2020  CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef PlaneGCSSolver_ScalarArrayWrapper_H_
+#define PlaneGCSSolver_ScalarArrayWrapper_H_
+
+#include <PlaneGCSSolver_Defs.h>
+#include <PlaneGCSSolver_EntityWrapper.h>
+
+/**
+ *  Wrapper providing operations with array of PlaneGCS scalars.
+ */
+class PlaneGCSSolver_ScalarArrayWrapper : public PlaneGCSSolver_EntityWrapper
+{
+public:
+  PlaneGCSSolver_ScalarArrayWrapper(const GCS::VEC_pD& theParam);
+
+  /// \breif Size of array
+  int size() const { return (int)myValue.size(); }
+
+  /// \brief Return array of PlaneGCS parameters
+  const GCS::VEC_pD& array() const { return myValue; }
+  /// \breif Set array of parameters
+  void setArray(const GCS::VEC_pD& theParams) { myValue = theParams; }
+
+  /// \brief Return type of current entity
+  virtual SketchSolver_EntityType type() const
+  { return ENTITY_SCALAR_ARRAY; }
+
+protected:
+  /// \brief Update entity by the values of theAttribute
+  /// \return \c true if any value of attribute is not equal to the stored in the entity
+  virtual bool update(std::shared_ptr<ModelAPI_Attribute> theAttribute);
+
+protected:
+  GCS::VEC_pD myValue; ///< list of pointers to values provided by the storage
+};
+
+typedef std::shared_ptr<PlaneGCSSolver_ScalarArrayWrapper> ScalarArrayWrapperPtr;
+
+#endif
index 439bc8184c4f85bbf557ac6b55d0b138cc185fb1..399b37531a88487e34ab413c6c40c13a80a23952 100644 (file)
 //
 
 #include <PlaneGCSSolver_ScalarWrapper.h>
+#include <PlaneGCSSolver_Tools.h>
 
-#include <cmath>
+#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeInteger.h>
 
 PlaneGCSSolver_ScalarWrapper::PlaneGCSSolver_ScalarWrapper(double *const theParam)
   : myValue(theParam)
@@ -35,3 +37,29 @@ double PlaneGCSSolver_ScalarWrapper::value() const
 {
   return *myValue;
 }
+
+bool PlaneGCSSolver_ScalarWrapper::update(AttributePtr theAttribute)
+{
+  double anAttrValue = 0.0;
+  AttributeDoublePtr aDouble =
+      std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
+  if (aDouble)
+    anAttrValue = aDouble->value();
+  else {
+    AttributeIntegerPtr anInt =
+        std::dynamic_pointer_cast<ModelAPI_AttributeInteger>(theAttribute);
+    if (anInt)
+      anAttrValue = anInt->value();
+    else
+      return false;
+  }
+
+  // There is possible an angular value, which is converted between degrees and radians.
+  // So, we use its value instead of using direct pointer to variable.
+  double aCurrentValue = value();
+
+  bool isUpdated = PlaneGCSSolver_Tools::updateValue(anAttrValue, aCurrentValue);
+  if (isUpdated)
+    setValue(aCurrentValue);
+  return isUpdated;
+}
index 454f5ea01159642431862ca71c6022d090ed7fa3..01238d579d2ed795920d6f5886de1f3a294fba85 100644 (file)
@@ -44,6 +44,11 @@ public:
   virtual SketchSolver_EntityType type() const
   { return ENTITY_SCALAR; }
 
+protected:
+  /// \brief Update entity by the values of theAttribute
+  /// \return \c true if any value of attribute is not equal to the stored in the entity
+  virtual bool update(std::shared_ptr<ModelAPI_Attribute> theAttribute);
+
 protected:
   double* myValue; ///< pointer to value provided by the storage
 };
index a5788f23a82f8e37224d769a52f4c91cf07bc89e..7f936524c083487d9dac8719243404b4d14ce5b3 100644 (file)
@@ -23,6 +23,8 @@
 #include <PlaneGCSSolver_ConstraintWrapper.h>
 #include <PlaneGCSSolver_EdgeWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_PointArrayWrapper.h>
+#include <PlaneGCSSolver_ScalarArrayWrapper.h>
 #include <PlaneGCSSolver_Tools.h>
 
 #include <PlaneGCSSolver_AttributeBuilder.h>
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_XY.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
+#include <ModelAPI_AttributeDoubleArray.h>
 #include <ModelAPI_AttributeRefAttr.h>
+#include <SketchPlugin_BSpline.h>
 #include <SketchPlugin_Ellipse.h>
 #include <SketchPlugin_Projection.h>
 
@@ -102,55 +107,6 @@ EntityWrapperPtr PlaneGCSSolver_Storage::createAttribute(
   return aResult;
 }
 
-/// \brief Update value
-static bool updateValue(const double& theSource, double& theDest)
-{
-  static const double aTol = 1.e4 * tolerance;
-  bool isUpdated = fabs(theSource - theDest) > aTol;
-  if (isUpdated)
-    theDest = theSource;
-  return isUpdated;
-}
-
-/// \brief Update coordinates of the point or scalar using its base attribute
-static bool updateValues(AttributePtr& theAttribute, EntityWrapperPtr& theEntity)
-{
-  bool isUpdated = false;
-
-  std::shared_ptr<GeomDataAPI_Point2D> aPoint2D =
-      std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
-  if (aPoint2D) {
-    const GCSPointPtr& aGCSPoint =
-        std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(theEntity)->point();
-    isUpdated = updateValue(aPoint2D->x(), *(aGCSPoint->x)) || isUpdated;
-    isUpdated = updateValue(aPoint2D->y(), *(aGCSPoint->y)) || isUpdated;
-  } else {
-    AttributeDoublePtr aScalar =
-        std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(theAttribute);
-    if (aScalar) {
-      ScalarWrapperPtr aWrapper =
-          std::dynamic_pointer_cast<PlaneGCSSolver_ScalarWrapper>(theEntity);
-      // There is possible angular value, which is converted between degrees and radians.
-      // So, we use its value instead of using direct pointer to value.
-      double aValue = aWrapper->value();
-      isUpdated = updateValue(aScalar->value(), aValue);
-      if (isUpdated)
-        aWrapper->setValue(aValue);
-    } else {
-      AttributeBooleanPtr aBoolean =
-          std::dynamic_pointer_cast<ModelAPI_AttributeBoolean>(theAttribute);
-      if (aBoolean) {
-        BooleanWrapperPtr aWrapper =
-            std::dynamic_pointer_cast<PlaneGCSSolver_BooleanWrapper>(theEntity);
-        isUpdated = aWrapper->value() != aBoolean->value();
-        aWrapper->setValue(aBoolean->value());
-      }
-    }
-  }
-
-  return isUpdated;
-}
-
 static bool hasReference(std::shared_ptr<SketchPlugin_Feature> theFeature,
                          const std::string& theFeatureKind)
 {
@@ -203,9 +159,7 @@ bool PlaneGCSSolver_Storage::update(FeaturePtr theFeature, bool theForce)
   std::list<AttributePtr> anAttributes = theFeature->data()->attributes(std::string());
   std::list<AttributePtr>::iterator anAttrIt = anAttributes.begin();
   for (; anAttrIt != anAttributes.end(); ++anAttrIt)
-    if ((*anAttrIt)->attributeType() == GeomDataAPI_Point2D::typeId() ||
-        (*anAttrIt)->attributeType() == ModelAPI_AttributeDouble::typeId() ||
-        (*anAttrIt)->attributeType() == ModelAPI_AttributeBoolean::typeId())
+    if (PlaneGCSSolver_Tools::isAttributeApplicable((*anAttrIt)->id(), theFeature->getKind()))
       isUpdated = update(*anAttrIt) || isUpdated;
 
   // check external attribute is changed
@@ -257,7 +211,8 @@ bool PlaneGCSSolver_Storage::update(AttributePtr theAttribute, bool theForce)
     return aRelated.get() != 0;
   }
 
-  bool isUpdated = updateValues(anAttribute, aRelated);
+  PlaneGCSSolver_AttributeBuilder aBuilder(aRelated->isExternal() ? 0 : this);
+  bool isUpdated = aBuilder.updateAttribute(anAttribute, aRelated);
   if (isUpdated) {
     setNeedToResolve(true);
     notify(aFeature);
@@ -413,6 +368,47 @@ static void createEllipticArcConstraints(
   constraintsToSolver(aConstraint, theSolver);
 }
 
+static void createBSplineConstraints(
+    const EntityWrapperPtr& theCurve,
+    const SolverPtr& theSolver,
+    const ConstraintID theConstraintID,
+    std::map<EntityWrapperPtr, ConstraintWrapperPtr>& theConstraints)
+{
+  // set start and end point of B-spline equal to first and last pole correspondingly
+  EdgeWrapperPtr anEdge = std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(theCurve);
+  std::shared_ptr<GCS::BSpline> aBSpline =
+      std::dynamic_pointer_cast<GCS::BSpline>(anEdge->entity());
+  if (aBSpline->periodic)
+    return; // additional constraints are not necessary
+
+  std::list<GCSConstraintPtr> aBSplineConstraints;
+
+  const std::map<std::string, EntityWrapperPtr>& anAdditional = anEdge->additionalAttributes();
+  PointWrapperPtr aStartPoint = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(
+      anAdditional.at(SketchPlugin_BSpline::START_ID()));
+
+  const GCS::Point& sp = *aStartPoint->point();
+  const GCS::Point& p0 = aBSpline->poles.front();
+  aBSplineConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintEqual(p0.x, sp.x)));
+  aBSplineConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintEqual(p0.y, sp.y)));
+
+  PointWrapperPtr aEndPoint = std::dynamic_pointer_cast<PlaneGCSSolver_PointWrapper>(
+      anAdditional.at(SketchPlugin_BSpline::END_ID()));
+
+  const GCS::Point& ep = *aEndPoint->point();
+  const GCS::Point& pN = aBSpline->poles.back();
+  aBSplineConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintEqual(pN.x, ep.x)));
+  aBSplineConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintEqual(pN.y, ep.y)));
+
+  ConstraintWrapperPtr aWrapper(
+      new PlaneGCSSolver_ConstraintWrapper(aBSplineConstraints, CONSTRAINT_UNKNOWN));
+  aWrapper->setId(theConstraintID);
+  if (theSolver)
+    constraintsToSolver(aWrapper, theSolver);
+
+  theConstraints[theCurve] = aWrapper;
+}
+
 void PlaneGCSSolver_Storage::createAuxiliaryConstraints(const EntityWrapperPtr& theEntity)
 {
   if (!theEntity || theEntity->isExternal())
@@ -426,6 +422,8 @@ void PlaneGCSSolver_Storage::createAuxiliaryConstraints(const EntityWrapperPtr&
     createEllipticArcConstraints(theEntity, mySketchSolver,
                                  ++myConstraintLastID, myAuxConstraintMap);
   }
+  else if (theEntity->type() == ENTITY_BSPLINE)
+    createBSplineConstraints(theEntity, mySketchSolver, ++myConstraintLastID, myAuxConstraintMap);
 }
 
 void PlaneGCSSolver_Storage::removeAuxiliaryConstraints(const EntityWrapperPtr& theEntity)
@@ -578,6 +576,8 @@ double* PlaneGCSSolver_Storage::createParameter()
 void PlaneGCSSolver_Storage::removeParameters(const GCS::SET_pD& theParams)
 {
   mySketchSolver->removeParameters(theParams);
+  //for (GCS::SET_pD::iterator it = theParams.begin(); it != theParams.end(); ++it)
+  //  delete *it;
 }
 
 // indicates attribute containing in the external feature
@@ -626,6 +626,23 @@ void PlaneGCSSolver_Storage::refresh() const
       }
       continue;
     }
+    std::shared_ptr<GeomDataAPI_Point2DArray> aPointArray =
+        std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(anIt->first);
+    if (aPointArray) {
+      std::shared_ptr<PlaneGCSSolver_PointArrayWrapper> anArrayWrapper =
+          std::dynamic_pointer_cast<PlaneGCSSolver_PointArrayWrapper>(anIt->second);
+      int aSize = aPointArray->size();
+      for (int anIndex = 0; anIndex < aSize; ++anIndex) {
+        GeomPnt2dPtr anOriginal = aPointArray->pnt(anIndex);
+        GCSPointPtr aGCSPoint = anArrayWrapper->value(anIndex)->point();
+        if (fabs(anOriginal->x() - (*aGCSPoint->x)) > aTol ||
+            fabs(anOriginal->y() - (*aGCSPoint->y)) > aTol) {
+          aPointArray->setPnt(anIndex, *aGCSPoint->x, *aGCSPoint->y);
+          addOwnerToSet(anIt->first, anUpdatedFeatures);
+        }
+      }
+      continue;
+    }
     AttributeDoublePtr aScalar = std::dynamic_pointer_cast<ModelAPI_AttributeDouble>(anIt->first);
     if (aScalar) {
       ScalarWrapperPtr aScalarWrapper =
@@ -636,6 +653,20 @@ void PlaneGCSSolver_Storage::refresh() const
       }
       continue;
     }
+    AttributeDoubleArrayPtr aRealArray =
+        std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(anIt->first);
+    if (aRealArray) {
+      std::shared_ptr<PlaneGCSSolver_ScalarArrayWrapper> anArrayWrapper =
+          std::dynamic_pointer_cast<PlaneGCSSolver_ScalarArrayWrapper>(anIt->second);
+      int aSize = aRealArray->size();
+      for (int anIndex = 0; anIndex < aSize; ++anIndex) {
+        if (fabs(aRealArray->value(anIndex) - *anArrayWrapper->array()[anIndex]) > aTol) {
+          aRealArray->setValue(anIndex, *anArrayWrapper->array()[anIndex]);
+          addOwnerToSet(anIt->first, anUpdatedFeatures);
+        }
+      }
+      continue;
+    }
   }
 
   // notify listeners about features update
index a13a7b891b5e8f0a498f52edb457e64ba9d7f7cf..e29d23018fba251e44f77bb27672d4e8e695456d 100644 (file)
@@ -19,7 +19,9 @@
 
 #include <PlaneGCSSolver_Tools.h>
 #include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointArrayWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
+#include <PlaneGCSSolver_ScalarArrayWrapper.h>
 #include <PlaneGCSSolver_ScalarWrapper.h>
 #include <PlaneGCSSolver_ConstraintWrapper.h>
 
 #include <SketchSolver_ConstraintMultiRotation.h>
 #include <SketchSolver_ConstraintMultiTranslation.h>
 
+#include <SketchPlugin_Arc.h>
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
+#include <SketchPlugin_Circle.h>
 #include <SketchPlugin_ConstraintAngle.h>
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintCollinear.h>
 #include <SketchPlugin_ConstraintRigid.h>
 #include <SketchPlugin_ConstraintPerpendicular.h>
 #include <SketchPlugin_ConstraintTangent.h>
+#include <SketchPlugin_Ellipse.h>
+#include <SketchPlugin_EllipticArc.h>
 #include <SketchPlugin_Line.h>
 #include <SketchPlugin_MultiRotation.h>
 #include <SketchPlugin_MultiTranslation.h>
+#include <SketchPlugin_Point.h>
 
 #include <GeomAPI_Circ2d.h>
 #include <GeomAPI_Dir2d.h>
@@ -62,6 +71,9 @@
 #include <GeomAPI_Lin2d.h>
 #include <GeomAPI_Pnt2d.h>
 
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeIntArray.h>
+
 #include <cmath>
 
 
@@ -77,7 +89,8 @@ static ConstraintWrapperPtr
 static ConstraintWrapperPtr
   createConstraintPointOnEntity(const SketchSolver_ConstraintType& theType,
                                 std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
-                                std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity);
+                                std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity,
+                                std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue);
 static ConstraintWrapperPtr
   createConstraintPointsCollinear(std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint1,
                                   std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint2,
@@ -128,15 +141,15 @@ static ConstraintWrapperPtr
                                      std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2);
 
 static GCS::SET_pD scalarParameters(const ScalarWrapperPtr& theScalar);
+static GCS::SET_pD scalarArrayParameters(const EntityWrapperPtr& theArray);
 static GCS::SET_pD pointParameters(const PointWrapperPtr& thePoint);
+static GCS::SET_pD pointArrayParameters(const EntityWrapperPtr& theArray);
 static GCS::SET_pD lineParameters(const EdgeWrapperPtr& theLine);
 static GCS::SET_pD circleParameters(const EdgeWrapperPtr& theCircle);
 static GCS::SET_pD arcParameters(const EdgeWrapperPtr& theArc);
 static GCS::SET_pD ellipseParameters(const EdgeWrapperPtr& theEllipse);
 static GCS::SET_pD ellipticArcParameters(const EdgeWrapperPtr& theEllipticArc);
-
-static double distance(const GCS::Point& thePnt1, const GCS::Point& thePnt2);
-
+static GCS::SET_pD bsplineParameters(const EdgeWrapperPtr& theEdge);
 
 
 
@@ -191,6 +204,13 @@ std::shared_ptr<SketchSolver_ConstraintMovement> PlaneGCSSolver_Tools::createMov
       new SketchSolver_ConstraintMovement(theMovedAttribute));
 }
 
+std::shared_ptr<SketchSolver_ConstraintMovement> PlaneGCSSolver_Tools::createMovementConstraint(
+    const std::pair<AttributePtr, int>& theMovedPointInArray)
+{
+  return std::shared_ptr<SketchSolver_ConstraintMovement>(
+      new SketchSolver_ConstraintMovement(theMovedPointInArray.first, theMovedPointInArray.second));
+}
+
 
 
 ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
@@ -214,8 +234,9 @@ ConstraintWrapperPtr PlaneGCSSolver_Tools::createConstraint(
     aResult = createConstraintCoincidence(aPoint1, aPoint2);
     break;
   case CONSTRAINT_PT_ON_CURVE:
-    aResult = anEntity1 ? createConstraintPointOnEntity(theType, aPoint1, anEntity1):
-              createConstraintPointsCollinear(aPoint1, aPoint2, GCS_POINT_WRAPPER(theEntity1));
+    aResult = anEntity1 ?
+        createConstraintPointOnEntity(theType, aPoint1, anEntity1, GCS_SCALAR_WRAPPER(theValue)) :
+        createConstraintPointsCollinear(aPoint1, aPoint2, GCS_POINT_WRAPPER(theEntity1));
     break;
   case CONSTRAINT_MIDDLE_POINT:
     aResult = createConstraintMiddlePoint(aPoint1, GCS_EDGE_WRAPPER(theEntity1), aPoint2);
@@ -389,8 +410,12 @@ GCS::SET_pD PlaneGCSSolver_Tools::parameters(const EntityWrapperPtr& theEntity)
   case ENTITY_SCALAR:
   case ENTITY_ANGLE:
     return scalarParameters(GCS_SCALAR_WRAPPER(theEntity));
+  case ENTITY_SCALAR_ARRAY:
+    return scalarArrayParameters(theEntity);
   case ENTITY_POINT:
     return pointParameters(GCS_POINT_WRAPPER(theEntity));
+  case ENTITY_POINT_ARRAY:
+    return pointArrayParameters(theEntity);
   case ENTITY_LINE:
     return lineParameters(GCS_EDGE_WRAPPER(theEntity));
   case ENTITY_CIRCLE:
@@ -401,11 +426,119 @@ GCS::SET_pD PlaneGCSSolver_Tools::parameters(const EntityWrapperPtr& theEntity)
     return ellipseParameters(GCS_EDGE_WRAPPER(theEntity));
   case ENTITY_ELLIPTIC_ARC:
     return ellipticArcParameters(GCS_EDGE_WRAPPER(theEntity));
+  case ENTITY_BSPLINE:
+    return bsplineParameters(GCS_EDGE_WRAPPER(theEntity));
   default: break;
   }
   return GCS::SET_pD();
 }
 
+bool PlaneGCSSolver_Tools::isAttributeApplicable(const std::string& theAttrName,
+                                                 const std::string& theOwnerName)
+{
+  if (theOwnerName == SketchPlugin_Arc::ID()) {
+    return theAttrName == SketchPlugin_Arc::CENTER_ID() ||
+           theAttrName == SketchPlugin_Arc::START_ID() ||
+           theAttrName == SketchPlugin_Arc::END_ID() ||
+           theAttrName == SketchPlugin_Arc::REVERSED_ID();
+  }
+  else if (theOwnerName == SketchPlugin_Circle::ID()) {
+    return theAttrName == SketchPlugin_Circle::CENTER_ID() ||
+           theAttrName == SketchPlugin_Circle::RADIUS_ID();
+  }
+  else if (theOwnerName == SketchPlugin_Line::ID()) {
+    return theAttrName == SketchPlugin_Line::START_ID() ||
+           theAttrName == SketchPlugin_Line::END_ID();
+  }
+  else if (theOwnerName == SketchPlugin_Ellipse::ID()) {
+    return theAttrName == SketchPlugin_Ellipse::CENTER_ID() ||
+           theAttrName == SketchPlugin_Ellipse::FIRST_FOCUS_ID() ||
+           theAttrName == SketchPlugin_Ellipse::SECOND_FOCUS_ID() ||
+           theAttrName == SketchPlugin_Ellipse::MAJOR_AXIS_START_ID() ||
+           theAttrName == SketchPlugin_Ellipse::MAJOR_AXIS_END_ID() ||
+           theAttrName == SketchPlugin_Ellipse::MINOR_AXIS_START_ID() ||
+           theAttrName == SketchPlugin_Ellipse::MINOR_AXIS_END_ID() ||
+           theAttrName == SketchPlugin_Ellipse::MAJOR_RADIUS_ID() ||
+           theAttrName == SketchPlugin_Ellipse::MINOR_RADIUS_ID();
+  }
+  else if (theOwnerName == SketchPlugin_EllipticArc::ID()) {
+    return theAttrName == SketchPlugin_EllipticArc::CENTER_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::FIRST_FOCUS_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::SECOND_FOCUS_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::MAJOR_AXIS_START_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::MAJOR_AXIS_END_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::MINOR_AXIS_START_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::MINOR_AXIS_END_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::MAJOR_RADIUS_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::MINOR_RADIUS_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::START_POINT_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::END_POINT_ID() ||
+           theAttrName == SketchPlugin_EllipticArc::REVERSED_ID();
+  }
+  else if (theOwnerName == SketchPlugin_BSpline::ID()) {
+    return theAttrName == SketchPlugin_BSpline::POLES_ID() ||
+           theAttrName == SketchPlugin_BSpline::WEIGHTS_ID() ||
+           theAttrName == SketchPlugin_BSpline::KNOTS_ID() ||
+           theAttrName == SketchPlugin_BSpline::MULTS_ID() ||
+           theAttrName == SketchPlugin_BSpline::DEGREE_ID() ||
+           theAttrName == SketchPlugin_BSpline::START_ID() ||
+           theAttrName == SketchPlugin_BSpline::END_ID();
+  }
+  else if (theOwnerName == SketchPlugin_BSplinePeriodic::ID()) {
+    return theAttrName == SketchPlugin_BSplinePeriodic::POLES_ID() ||
+           theAttrName == SketchPlugin_BSplinePeriodic::WEIGHTS_ID() ||
+           theAttrName == SketchPlugin_BSplinePeriodic::KNOTS_ID() ||
+           theAttrName == SketchPlugin_BSplinePeriodic::MULTS_ID() ||
+           theAttrName == SketchPlugin_BSplinePeriodic::DEGREE_ID();
+  }
+
+  // suppose that all remaining features are points
+  return theAttrName == SketchPlugin_Point::COORD_ID();
+}
+
+/// \brief Update value
+bool PlaneGCSSolver_Tools::updateValue(const double& theSource, double& theDest,
+                                       const double theTolerance)
+{
+  bool isUpdated = fabs(theSource - theDest) > theTolerance;
+  if (isUpdated)
+    theDest = theSource;
+  return isUpdated;
+}
+
+
+double PlaneGCSSolver_Tools::distance(const GCS::Point& thePnt1, const GCS::Point& thePnt2)
+{
+  double x = *thePnt1.x - *thePnt2.x;
+  double y = *thePnt1.y - *thePnt2.y;
+  return sqrt(x*x + y*y);
+}
+
+
+
+
+// ================   AttributeArray methods   ==========================
+PlaneGCSSolver_Tools::AttributeArray::AttributeArray(AttributePtr theAttribute)
+{
+  myDouble = std::dynamic_pointer_cast<ModelAPI_AttributeDoubleArray>(theAttribute);
+  myInteger = std::dynamic_pointer_cast<ModelAPI_AttributeIntArray>(theAttribute);
+}
+
+bool PlaneGCSSolver_Tools::AttributeArray::isInitialized() const
+{
+  return (myDouble && myDouble->isInitialized()) || (myInteger && myInteger->isInitialized());
+}
+
+int PlaneGCSSolver_Tools::AttributeArray::size() const
+{
+  return myDouble.get() ? myDouble->size() : myInteger->size();
+}
+
+double PlaneGCSSolver_Tools::AttributeArray::value(const int theIndex) const
+{
+  return myDouble.get() ? myDouble->value(theIndex) : myInteger->value(theIndex);
+}
+
 
 
 
@@ -433,7 +566,8 @@ ConstraintWrapperPtr createConstraintCoincidence(
 ConstraintWrapperPtr createConstraintPointOnEntity(
     const SketchSolver_ConstraintType& theType,
     std::shared_ptr<PlaneGCSSolver_PointWrapper> thePoint,
-    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity)
+    std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity,
+    std::shared_ptr<PlaneGCSSolver_ScalarWrapper> theValue)
 {
   GCSConstraintPtr aNewConstr;
 
@@ -459,6 +593,14 @@ ConstraintWrapperPtr createConstraintPointOnEntity(
         new GCS::ConstraintPointOnEllipse(*(thePoint->point()), *anEllipse));
     break;
     }
+  case ENTITY_BSPLINE: {
+    std::list<GCSConstraintPtr> aConstraints;
+    aConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+        *thePoint->point(), thePoint->point()->x, *theEntity->entity(), theValue->scalar())));
+    aConstraints.push_back(GCSConstraintPtr(new GCS::ConstraintCurveValue(
+        *thePoint->point(), thePoint->point()->y, *theEntity->entity(), theValue->scalar())));
+    return ConstraintWrapperPtr(new PlaneGCSSolver_ConstraintWrapper(aConstraints, theType));
+  }
   default:
     return ConstraintWrapperPtr();
   }
@@ -602,19 +744,10 @@ ConstraintWrapperPtr createConstraintAngle(
     std::shared_ptr<PlaneGCSSolver_EdgeWrapper> theEntity2)
 {
   std::shared_ptr<GCS::Line> aLine1 = std::dynamic_pointer_cast<GCS::Line>(theEntity1->entity());
-  bool isLine1Rev = theConstraint->boolean(
-      SketchPlugin_ConstraintAngle::ANGLE_REVERSED_FIRST_LINE_ID())->value();
-  GCS::Point aLine1Pt1 = isLine1Rev ? aLine1->p2 : aLine1->p1;
-  GCS::Point aLine1Pt2 = isLine1Rev ? aLine1->p1 : aLine1->p2;
-
   std::shared_ptr<GCS::Line> aLine2 = std::dynamic_pointer_cast<GCS::Line>(theEntity2->entity());
-  bool isLine2Rev = theConstraint->boolean(
-      SketchPlugin_ConstraintAngle::ANGLE_REVERSED_SECOND_LINE_ID())->value();
-  GCS::Point aLine2Pt1 = isLine2Rev ? aLine2->p2 : aLine2->p1;
-  GCS::Point aLine2Pt2 = isLine2Rev ? aLine2->p1 : aLine2->p2;
 
   GCSConstraintPtr aNewConstr(new GCS::ConstraintL2LAngle(
-      aLine1Pt1, aLine1Pt2, aLine2Pt1, aLine2Pt2, theValue->scalar()));
+      aLine1->p1, aLine1->p2, aLine2->p1, aLine2->p2, theValue->scalar()));
 
   std::shared_ptr<PlaneGCSSolver_ConstraintWrapper> aResult(
       new PlaneGCSSolver_ConstraintWrapper(aNewConstr, CONSTRAINT_ANGLE));
@@ -707,7 +840,7 @@ ConstraintWrapperPtr createConstraintEqual(
     aConstrList.push_back(GCSConstraintPtr(
         new GCS::ConstraintP2PDistance(aLine2->p1, aLine2->p2, theIntermed->scalar())));
     // update value of intermediate parameter
-    theIntermed->setValue(distance(aLine1->p1, aLine1->p2));
+    theIntermed->setValue(PlaneGCSSolver_Tools::distance(aLine1->p1, aLine1->p2));
   }
   else if (theType == CONSTRAINT_EQUAL_ELLIPSES) {
     std::shared_ptr<GCS::Ellipse> anEllipse1 =
@@ -722,7 +855,7 @@ ConstraintWrapperPtr createConstraintEqual(
     aConstrList.push_back(GCSConstraintPtr(new GCS::ConstraintP2PDistance(
         anEllipse2->center, anEllipse2->focus1, theIntermed->scalar())));
     // update value of intermediate parameter
-    theIntermed->setValue(distance(anEllipse1->center, anEllipse1->focus1));
+    theIntermed->setValue(PlaneGCSSolver_Tools::distance(anEllipse1->center, anEllipse1->focus1));
   }
   else {
     std::shared_ptr<GCS::Circle> aCirc1 =
@@ -746,6 +879,13 @@ GCS::SET_pD scalarParameters(const ScalarWrapperPtr& theScalar)
   return aParams;
 }
 
+GCS::SET_pD scalarArrayParameters(const EntityWrapperPtr& theArray)
+{
+  ScalarArrayWrapperPtr anArray =
+      std::dynamic_pointer_cast<PlaneGCSSolver_ScalarArrayWrapper>(theArray);
+  return GCS::SET_pD(anArray->array().begin(), anArray->array().end());
+}
+
 GCS::SET_pD pointParameters(const PointWrapperPtr& thePoint)
 {
   GCS::SET_pD aParams;
@@ -754,6 +894,19 @@ GCS::SET_pD pointParameters(const PointWrapperPtr& thePoint)
   return aParams;
 }
 
+GCS::SET_pD pointArrayParameters(const EntityWrapperPtr& theArray)
+{
+  GCS::SET_pD aParams;
+  PointArrayWrapperPtr aPoints =
+      std::dynamic_pointer_cast<PlaneGCSSolver_PointArrayWrapper>(theArray);
+  for (std::vector<PointWrapperPtr>::const_iterator anIt = aPoints->array().begin();
+       anIt != aPoints->array().end(); ++anIt) {
+    GCS::SET_pD aPointParams = PlaneGCSSolver_Tools::parameters(*anIt);
+    aParams.insert(aPointParams.begin(), aPointParams.end());
+  }
+  return aParams;
+}
+
 GCS::SET_pD lineParameters(const EdgeWrapperPtr& theLine)
 {
   GCS::SET_pD aParams;
@@ -815,9 +968,19 @@ GCS::SET_pD ellipticArcParameters(const EdgeWrapperPtr& theEllipticArc)
   return aParams;
 }
 
-double distance(const GCS::Point& thePnt1, const GCS::Point& thePnt2)
+GCS::SET_pD bsplineParameters(const EdgeWrapperPtr& theEdge)
 {
-  double x = *thePnt1.x - *thePnt2.x;
-  double y = *thePnt1.y - *thePnt2.y;
-  return sqrt(x*x + y*y);
+  GCS::SET_pD aParams;
+
+  std::shared_ptr<GCS::BSpline> aBSpline =
+    std::dynamic_pointer_cast<GCS::BSpline>(theEdge->entity());
+
+  for (GCS::VEC_P::iterator it = aBSpline->poles.begin(); it != aBSpline->poles.end(); ++it) {
+    aParams.insert(it->x);
+    aParams.insert(it->y);
+  }
+  for (GCS::VEC_pD::iterator it = aBSpline->weights.begin(); it != aBSpline->weights.end(); ++it)
+    aParams.insert(*it);
+
+  return aParams;
 }
index a02a9948c77a2302f86a4b7d48176b33f3d3f4ea..2604d1541c706c082520e96cc9c84d63f18b88df 100644 (file)
@@ -29,6 +29,9 @@ class GeomAPI_Ellipse2d;
 class GeomAPI_Lin2d;
 class GeomAPI_Pnt2d;
 
+class ModelAPI_AttributeDoubleArray;
+class ModelAPI_AttributeIntArray;
+
 /** \namespace  PlaneGCSSolver_Tools
  *  \ingroup    Plugins
  *  \brief      Converter tools
@@ -45,6 +48,9 @@ namespace PlaneGCSSolver_Tools
   /// \brief Creates temporary constraint to fix the attribute after movement
   std::shared_ptr<SketchSolver_ConstraintMovement>
       createMovementConstraint(AttributePtr theMovedAttribute);
+  /// \brief Creates temporary constraint to fix the point in array after movement
+  std::shared_ptr<SketchSolver_ConstraintMovement>
+      createMovementConstraint(const std::pair<AttributePtr, int>& theMovedPointInArray);
 
   /// \brief Creates new constraint using given parameters
   /// \param theConstraint [in]  original constraint
@@ -64,6 +70,12 @@ namespace PlaneGCSSolver_Tools
                                         const EntityWrapperPtr& theEntity3 = EntityWrapperPtr(),
                                         const EntityWrapperPtr& theEntity4 = EntityWrapperPtr());
 
+  /// \brief Return \c true if the attribute is used in PlaneGCS solver
+  /// \param[in] theAttrName  name of the attribute
+  /// \param[in] theOwnerName name of the parent feature
+  bool isAttributeApplicable(const std::string& theAttrName,
+                             const std::string& theOwnerName);
+
   /// \brief Convert entity to point
   /// \return empty pointer if the entity is not a point
   std::shared_ptr<GeomAPI_Pnt2d> point(EntityWrapperPtr theEntity);
@@ -88,6 +100,30 @@ namespace PlaneGCSSolver_Tools
 
   /// brief Return list of parameters for the given entity
   GCS::SET_pD parameters(const EntityWrapperPtr& theEntity);
+
+  /// \brief Update value in theDest if theSource is differ more than theTolerance
+  /// \return \c true if the value was updated.
+  bool updateValue(const double& theSource, double& theDest,
+                   const double theTolerance = 1.e4 * tolerance);
+
+  double distance(const GCS::Point& thePnt1, const GCS::Point& thePnt2);
+
+  /// \brief Provide an interface to access values in attribute which is an array of values
+  class AttributeArray
+  {
+  public:
+    AttributeArray(AttributePtr theAttribute);
+
+    bool isInitialized() const;
+
+    int size() const;
+
+    double value(const int theIndex) const;
+
+  private:
+    std::shared_ptr<ModelAPI_AttributeDoubleArray> myDouble;
+    std::shared_ptr<ModelAPI_AttributeIntArray> myInteger;
+  };
 };
 
 #endif
index 48c0fa5b80d63454f15ee4b12fa0a563c4b941d7..17720fdc4c3855a4a995e25075523c5510aa17d9 100644 (file)
@@ -233,7 +233,7 @@ void SketchSolver_Constraint::getAttributes(
     SketchSolver_EntityType aType = anEntity->type();
     if (aType == ENTITY_UNKNOWN)
       continue;
-    else if (aType == ENTITY_POINT)
+    else if (aType == ENTITY_POINT || aType == ENTITY_POINT_ARRAY)
       theAttributes[aPtInd++] = anEntity; // the point is created
     else { // another entity (not a point) is created
       if (aEntInd < anInitNbOfAttr)
index 4bac7dd1fda160092024b66cad9bc7fc5ed99d92..c00dcfce8d1ccd609a33d9083c1e9d74fad0d69a 100644 (file)
 
 #include <SketchSolver_ConstraintCoincidence.h>
 #include <SketchSolver_Error.h>
+#include <PlaneGCSSolver_PointArrayWrapper.h>
+#include <PlaneGCSSolver_Storage.h>
 #include <PlaneGCSSolver_Tools.h>
 #include <PlaneGCSSolver_UpdateCoincidence.h>
 
 #include <GeomDataAPI_Point2D.h>
 
+#include <ModelAPI_AttributeInteger.h>
+
 #include <SketchPlugin_Arc.h>
 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
 #include <SketchPlugin_Ellipse.h>
@@ -186,6 +190,20 @@ static void processEllipticArcExtremities(SketchSolver_ConstraintType& theType,
   }
 }
 
+static void getPointFromArray(EntityWrapperPtr& theArray,
+                              const ConstraintPtr& theConstraint,
+                              const std::string& theIndexAttrId)
+{
+  if (theArray && theArray->type() == ENTITY_POINT_ARRAY) {
+    AttributeIntegerPtr anIndexAttr = theConstraint->integer(theIndexAttrId);
+    if (anIndexAttr) {
+      PointArrayWrapperPtr aPointsArray =
+          std::dynamic_pointer_cast<PlaneGCSSolver_PointArrayWrapper>(theArray);
+      theArray = aPointsArray->value(anIndexAttr->value());
+    }
+  }
+}
+
 
 void SketchSolver_ConstraintCoincidence::process()
 {
@@ -207,7 +225,7 @@ void SketchSolver_ConstraintCoincidence::process()
 
   mySolverConstraint = PlaneGCSSolver_Tools::createConstraint(
       myBaseConstraint, getType(),
-      aValue, anAttributes[0], anAttributes[1], anAttributes[2], anAttributes[3]);
+      myAuxValue, anAttributes[0], anAttributes[1], anAttributes[2], anAttributes[3]);
 
   myStorage->subscribeUpdates(this, PlaneGCSSolver_UpdateCoincidence::GROUP());
   myStorage->notify(myBaseConstraint);
@@ -218,6 +236,13 @@ bool SketchSolver_ConstraintCoincidence::remove()
   myInSolver = false;
   myFeatureExtremities[0] = EntityWrapperPtr();
   myFeatureExtremities[1] = EntityWrapperPtr();
+  if (myAuxValue) {
+    std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+        std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+    GCS::SET_pD aParams;
+    aParams.insert(myAuxValue->scalar());
+    aStorage->removeParameters(aParams);
+  }
   return SketchSolver_Constraint::remove();
 }
 
@@ -240,10 +265,24 @@ void SketchSolver_ConstraintCoincidence::getAttributes(
                                   theAttributes, myFeatureExtremities);
   } else if (theAttributes[2]) {
     myType = CONSTRAINT_PT_ON_CURVE;
-    // obtain extremity points of the coincident feature for further checking of multi-coincidence
-    getCoincidentFeatureExtremities(myBaseConstraint, myStorage, myFeatureExtremities);
+    // point-on-bspline requires additional parameter
+    if (theAttributes[2]->type() == ENTITY_BSPLINE) {
+      std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+          std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+      myAuxValue.reset(new PlaneGCSSolver_ScalarWrapper(aStorage->createParameter()));
+    }
+    else {
+      // obtain extremity points of the coincident feature for further checking of multi-coincidence
+      getCoincidentFeatureExtremities(myBaseConstraint, myStorage, myFeatureExtremities);
+    }
   } else
     myErrorMsg = SketchSolver_Error::INCORRECT_ATTRIBUTE();
+
+  // process internal coincidence with a point in the array of points
+  getPointFromArray(theAttributes[0], myBaseConstraint,
+                    SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A());
+  getPointFromArray(theAttributes[1], myBaseConstraint,
+                    SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
 }
 
 void SketchSolver_ConstraintCoincidence::notify(const FeaturePtr&      theFeature,
@@ -300,3 +339,18 @@ void SketchSolver_ConstraintCoincidence::notify(const FeaturePtr&      theFeatur
     }
   }
 }
+
+void SketchSolver_ConstraintCoincidence::adjustConstraint()
+{
+  if (myBaseConstraint->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID()) {
+    AttributeIntegerPtr anIndexA = myBaseConstraint->integer(
+        SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A());
+    AttributeIntegerPtr anIndexB = myBaseConstraint->integer(
+        SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
+    if ((anIndexA && anIndexA->isInitialized()) ||
+        (anIndexB && anIndexB->isInitialized())) {
+      remove();
+      process();
+    }
+  }
+}
index 871314b926afa2d6993b1667e725eb66158eb3d7..4ed2b821c7825e285eb503919ebe9d86eeaec565 100644 (file)
@@ -52,9 +52,14 @@ protected:
   virtual void getAttributes(EntityWrapperPtr&              theValue,
                              std::vector<EntityWrapperPtr>& theAttributes);
 
+  /// \brief This method is used in derived objects to check consistency of constraint.
+  ///        E.g. the distance between line and point may be signed.
+  virtual void adjustConstraint();
+
 protected:
   bool myInSolver; ///< shows the constraint is added to the solver
   EntityWrapperPtr myFeatureExtremities[2]; ///< extremities of a feature, a point is coincident to
+  ScalarWrapperPtr myAuxValue; ///< parameter on B-spline curve
 };
 
 #endif
index a32e1966f49a200ca6016081b1c8e071062ab166..a56004115d8a3bef5d8342994b6ba3e737c4e9a1 100644 (file)
@@ -163,6 +163,16 @@ GCS::VEC_pD toParameters(const EntityWrapperPtr& theEntity)
     aParameters.push_back(anEllArc->endAngle);
     break;
     }
+  case ENTITY_BSPLINE: {
+    std::shared_ptr<GCS::BSpline> aBSpline =
+        std::dynamic_pointer_cast<GCS::BSpline>(anEntity->entity());
+    for (GCS::VEC_P::iterator anIt = aBSpline->poles.begin();
+         anIt != aBSpline->poles.end(); ++anIt) {
+      aParameters.push_back(anIt->x);
+      aParameters.push_back(anIt->y);
+    }
+    break;
+  }
   default:
     break;
   }
index 8e38fa6b8303487f7acead82f677d97b0c9b0660..3384416c7102c4ef375157b9c3d8907f5212d953 100644 (file)
@@ -22,6 +22,7 @@
 #include <SketchSolver_Manager.h>
 
 #include <PlaneGCSSolver_EdgeWrapper.h>
+#include <PlaneGCSSolver_PointArrayWrapper.h>
 #include <PlaneGCSSolver_PointWrapper.h>
 
 #include <SketchPlugin_Arc.h>
@@ -53,12 +54,14 @@ SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(FeaturePtr theF
 {
 }
 
-SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(AttributePtr thePoint)
+SketchSolver_ConstraintMovement::SketchSolver_ConstraintMovement(AttributePtr theAttribute,
+                                                                 const int thePointIndex)
   : SketchSolver_ConstraintFixed(ConstraintPtr()),
-    myDraggedPoint(thePoint),
+    myDraggedAttribute(theAttribute),
+    myDraggedPointIndex(thePointIndex),
     mySimpleMove(true)
 {
-  myMovedFeature = ModelAPI_Feature::feature(thePoint->owner());
+  myMovedFeature = ModelAPI_Feature::feature(theAttribute->owner());
 }
 
 void SketchSolver_ConstraintMovement::blockEvents(bool isBlocked)
@@ -115,22 +118,27 @@ ConstraintWrapperPtr SketchSolver_ConstraintMovement::initMovement()
     return aConstraint;
   }
 
-  EntityWrapperPtr anEntity =
-      myDraggedPoint ? myStorage->entity(myDraggedPoint) : myStorage->entity(myMovedFeature);
+  EntityWrapperPtr anEntity = myDraggedAttribute ? myStorage->entity(myDraggedAttribute)
+                                                 : myStorage->entity(myMovedFeature);
   if (!anEntity) {
     myStorage->update(myMovedFeature, true);
-    anEntity =
-        myDraggedPoint ? myStorage->entity(myDraggedPoint) : myStorage->entity(myMovedFeature);
+    anEntity = myDraggedAttribute ? myStorage->entity(myDraggedAttribute)
+                                  : myStorage->entity(myMovedFeature);
     if (!anEntity)
       return aConstraint;
   }
 
-  mySimpleMove = isSimpleMove(myMovedFeature, myDraggedPoint);
+  mySimpleMove = isSimpleMove(myMovedFeature, myDraggedAttribute);
 
-  if (mySimpleMove)
+  if (mySimpleMove) {
+    if (anEntity->type() == ENTITY_POINT_ARRAY) {
+      anEntity = std::dynamic_pointer_cast<PlaneGCSSolver_PointArrayWrapper>(anEntity)
+                 ->value(myDraggedPointIndex);
+    }
     aConstraint = fixFeature(anEntity);
+  }
   else {
-    if (myDraggedPoint) // start or end point of arc has been moved
+    if (myDraggedAttribute) // start or end point of arc has been moved
       aConstraint = fixArcExtremity(anEntity);
     else if (anEntity->type() == ENTITY_CIRCLE || anEntity->type() == ENTITY_ARC) {
       // arc or circle has been moved
@@ -311,7 +319,8 @@ void SketchSolver_ConstraintMovement::moveTo(
 #ifdef CHANGE_RADIUS_WHILE_MOVE
   int aMaxSize = mySimpleMove ? (int)myFixedValues.size() : 2;
 #else
-  int aMaxSize = myMovedFeature->getKind() == SketchPlugin_Line::ID() && !myDraggedPoint ? 4 : 2;
+  int aMaxSize =
+      myMovedFeature->getKind() == SketchPlugin_Line::ID() && !myDraggedAttribute ? 4 : 2;
 #endif
   for (int i = 0; i < aMaxSize; ++i)
     myFixedValues[i] += aDelta[i % 2];
index bfbe1f1ed644f2fc442e11ef918c4cd33275ebcc..451ddb462ee41672e0b9d4cbecafb0df4dd406c9 100644 (file)
@@ -39,7 +39,7 @@ public:
   SketchSolver_ConstraintMovement(FeaturePtr theFeature);
 
   /// Creates movement constraint based on point
-  SketchSolver_ConstraintMovement(AttributePtr thePoint);
+  SketchSolver_ConstraintMovement(AttributePtr theMovedAttribute, const int thePointIndex = -1);
 
   /// \brief Set coordinates of the start point of the movement
   void startPoint(const std::shared_ptr<GeomAPI_Pnt2d>& theStartPoint);
@@ -74,7 +74,8 @@ protected:
 
 private:
   FeaturePtr       myMovedFeature; ///< fixed feature (if set, myBaseConstraint should be NULL)
-  AttributePtr     myDraggedPoint; ///< one of the feature points which has been moved
+  AttributePtr     myDraggedAttribute; ///< one of the feature points which has been moved
+  int              myDraggedPointIndex; ///< index of the point if the moved attribute is an array
   std::shared_ptr<GeomAPI_Pnt2d> myStartPoint; ///< start point of the movement
 
   bool mySimpleMove; ///< simple move, thus all parameters should be increased by movement delta
index 18d95f8ec9d5c533dcaf88caf62f091719155b7b..f3832c24521f674cbd626acfe6815bf18710d33e 100644 (file)
 #include <GeomAPI_Pnt2d.h>
 #include <GeomAPI_Ellipse2d.h>
 
+#include <ModelAPI_AttributeInteger.h>
+
 #include <SketchPlugin_Arc.h>
+#include <SketchPlugin_BSpline.h>
 #include <SketchPlugin_Circle.h>
 #include <SketchPlugin_ConstraintCoincidence.h>
 #include <SketchPlugin_ConstraintCoincidenceInternal.h>
@@ -112,14 +115,20 @@ void SketchSolver_ConstraintTangent::rebuild()
   mySharedPoint = AttributePtr();
   if (myAuxPoint) {
     GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
+    if (myAuxParameters[0])
+      aParams.insert(myAuxParameters[0]->scalar());
+    if (myAuxParameters[1])
+      aParams.insert(myAuxParameters[1]->scalar());
     aStorage->removeParameters(aParams);
     myAuxPoint = EntityWrapperPtr();
+    myAuxParameters[0] = myAuxParameters[1] = ScalarWrapperPtr();
   }
 
   // Check the quantity of entities of each type and their order (arcs first)
   int aNbLines = 0;
   int aNbCircles = 0;
   int aNbEllipses = 0;
+  int aNbSplines = 0;
   std::list<EntityWrapperPtr>::iterator anEntIt = myAttributes.begin();
   for (; anEntIt != myAttributes.end(); ++anEntIt) {
     if (!(*anEntIt).get())
@@ -130,16 +139,18 @@ void SketchSolver_ConstraintTangent::rebuild()
       ++aNbCircles;
     else if ((*anEntIt)->type() == ENTITY_ELLIPSE || (*anEntIt)->type() == ENTITY_ELLIPTIC_ARC)
       ++aNbEllipses;
+    else if ((*anEntIt)->type() == ENTITY_BSPLINE)
+      ++aNbSplines;
   }
 
-  if (aNbCircles + aNbEllipses < 1) {
+  if (aNbCircles + aNbEllipses + aNbSplines < 1) {
     myErrorMsg = SketchSolver_Error::INCORRECT_TANGENCY_ATTRIBUTE();
     return;
   }
   if (aNbLines == 1 && aNbCircles == 1) {
     myType = CONSTRAINT_TANGENT_CIRCLE_LINE;
   }
-  else if (aNbLines + aNbCircles + aNbEllipses == 2) {
+  else if (aNbLines + aNbCircles + aNbEllipses + aNbSplines == 2) {
     myType = CONSTRAINT_TANGENT_CURVE_CURVE;
     isArcArcInternal = isArcArcTangencyInternal(myAttributes.front(), myAttributes.back());
   }
@@ -148,20 +159,61 @@ void SketchSolver_ConstraintTangent::rebuild()
     return;
   }
 
-  FeaturePtr aFeature1, aFeature2;
-  getTangentFeatures(myBaseConstraint, aFeature1, aFeature2);
+  FeaturePtr aFeatures[2];
+  getTangentFeatures(myBaseConstraint, aFeatures[0], aFeatures[1]);
 
   // check number of coincident points
-  std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeature1, aFeature2);
+  std::set<AttributePtr> aCoincidentPoints = coincidentBoundaryPoints(aFeatures[0], aFeatures[1]);
   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE && aCoincidentPoints.size() > 2) {
     myErrorMsg = SketchSolver_Error::TANGENCY_FAILED();
     return;
   }
 
-  // Try to find non-boundary points coincident with both features.
-  // It is necesasry to create tangency with ellipse
-  if (aCoincidentPoints.empty() && aNbEllipses > 0)
-    aCoincidentPoints = coincidentPoints(aFeature1, aFeature2);
+  EntityWrapperPtr aTgEntities[2] = { myAttributes.front(), myAttributes.back() };
+
+  if (aCoincidentPoints.empty()) {
+    // Try to find non-boundary points coincident with both features.
+    // It is necesasry to create tangency with ellipse.
+    if (aNbEllipses > 0)
+      aCoincidentPoints = coincidentPoints(aFeatures[0], aFeatures[1]);
+  }
+  else if (aNbSplines > 0) {
+    // General approach applying tangency to B-spline leads to hang-up in PlaneGCS.
+    // So, the tangency will be applied for the construction segment instead of B-spline curve.
+    for (int i = 0; i < 2; ++i) {
+      if (aTgEntities[i]->type() == ENTITY_BSPLINE) {
+        EdgeWrapperPtr anEdge =
+            std::dynamic_pointer_cast<PlaneGCSSolver_EdgeWrapper>(aTgEntities[i]);
+        std::shared_ptr<GCS::BSpline> aBSpline =
+            std::dynamic_pointer_cast<GCS::BSpline>(anEdge->entity());
+
+        // which boundary is coincident?
+        GCS::Point aPoint1, aPoint2;
+        for (std::set<AttributePtr>::iterator aPIt = aCoincidentPoints.begin();
+             aPIt != aCoincidentPoints.end(); ++aPIt) {
+          if ((*aPIt)->owner() == aFeatures[i]) {
+            if ((*aPIt)->id() == SketchPlugin_BSpline::START_ID()) {
+              aPoint1 = aBSpline->poles[0];
+              aPoint2 = aBSpline->poles[1];
+            }
+            else if ((*aPIt)->id() == SketchPlugin_BSpline::END_ID()) {
+              aPoint1 = aBSpline->poles[aBSpline->poles.size() - 2];
+              aPoint2 = aBSpline->poles[aBSpline->poles.size() - 1];
+            }
+            break;
+          }
+        }
+
+        // substitute B-spline by its boundary segment
+        std::shared_ptr<GCS::Line> aSegment(new GCS::Line);
+        aSegment->p1 = aPoint1;
+        aSegment->p2 = aPoint2;
+        aTgEntities[i] = EdgeWrapperPtr(new PlaneGCSSolver_EdgeWrapper(aSegment));
+        --aNbSplines;
+        ++aNbLines;
+      }
+    }
+  }
 
   EntityWrapperPtr aSharedPointEntity;
   std::list<GCSConstraintPtr> anAuxConstraints;
@@ -169,32 +221,36 @@ void SketchSolver_ConstraintTangent::rebuild()
     mySharedPoint = *aCoincidentPoints.begin();
     aSharedPointEntity = myStorage->entity(mySharedPoint);
   }
-  else if (aNbEllipses > 0) {
+  else if (aNbEllipses + aNbSplines > 0) {
     // create auxiliary point
     GCSPointPtr aPoint(new GCS::Point);
     aPoint->x = aStorage->createParameter();
     aPoint->y = aStorage->createParameter();
-    calculateTangencyPoint(myAttributes.front(), myAttributes.back(), aPoint);
+    calculateTangencyPoint(aTgEntities[0], aTgEntities[1], aPoint);
 
     myAuxPoint.reset(new PlaneGCSSolver_PointWrapper(aPoint));
     aSharedPointEntity = myAuxPoint;
 
-    // create auxiliary coincident constraints for tangency with ellipse
     EntityWrapperPtr aDummy;
-    ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
-        CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.front(), aDummy);
-    anAuxConstraints = aCoincidence->constraints();
-    aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
-        CONSTRAINT_PT_ON_CURVE, aDummy, aSharedPointEntity, aDummy, myAttributes.back(), aDummy);
-    anAuxConstraints.insert(anAuxConstraints.end(),
-        aCoincidence->constraints().begin(), aCoincidence->constraints().end());
+    for (int i = 0; i < 2; ++i) {
+      // create auxiliary parameters for coincidence with B-spline
+      if (aTgEntities[i]->type() == ENTITY_BSPLINE)
+        myAuxParameters[i].reset(new PlaneGCSSolver_ScalarWrapper(aStorage->createParameter()));
+
+      // create auxiliary coincident constraints for tangency with ellipse
+      ConstraintWrapperPtr aCoincidence = PlaneGCSSolver_Tools::createConstraint(ConstraintPtr(),
+          CONSTRAINT_PT_ON_CURVE, myAuxParameters[i],
+          aSharedPointEntity, aDummy, aTgEntities[i], aDummy);
+      anAuxConstraints.insert(anAuxConstraints.end(),
+          aCoincidence->constraints().begin(), aCoincidence->constraints().end());
+    }
   }
 
   if (myType == CONSTRAINT_TANGENT_CIRCLE_LINE) {
-    mySolverConstraint = createArcLineTangency(myAttributes.front(), myAttributes.back(),
+    mySolverConstraint = createArcLineTangency(aTgEntities[0], aTgEntities[1],
                                            aSharedPointEntity, &myCurveCurveAngle);
   } else {
-    mySolverConstraint = createCurveCurveTangency(myAttributes.front(), myAttributes.back(),
+    mySolverConstraint = createCurveCurveTangency(aTgEntities[0], aTgEntities[1],
                             isArcArcInternal, aSharedPointEntity, &myCurveCurveAngle);
   }
 
@@ -282,6 +338,24 @@ void SketchSolver_ConstraintTangent::notify(const FeaturePtr&      theFeature,
     rebuild();
 }
 
+bool SketchSolver_ConstraintTangent::remove()
+{
+  if (myAuxPoint) {
+    std::shared_ptr<PlaneGCSSolver_Storage> aStorage =
+        std::dynamic_pointer_cast<PlaneGCSSolver_Storage>(myStorage);
+
+    GCS::SET_pD aParams = PlaneGCSSolver_Tools::parameters(myAuxPoint);
+    if (myAuxParameters[0])
+      aParams.insert(myAuxParameters[0]->scalar());
+    if (myAuxParameters[1])
+      aParams.insert(myAuxParameters[1]->scalar());
+    aStorage->removeParameters(aParams);
+    myAuxPoint = EntityWrapperPtr();
+    myAuxParameters[0] = myAuxParameters[1] = ScalarWrapperPtr();
+  }
+  return SketchSolver_Constraint::remove();
+}
+
 
 
 
@@ -302,13 +376,22 @@ std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theF
   const std::set<AttributePtr>& aRefs2 = theFeature2->data()->refsToMe();
 
   std::set<FeaturePtr> aCoincidences;
+  std::map<AttributePtr, FeaturePtr> aCoincidentPoints;
   std::set<AttributePtr>::const_iterator anIt;
 
   // collect coincidences referred to the first feature
   for (anIt = aRefs1.begin(); anIt != aRefs1.end(); ++anIt) {
     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
-    if (aRef && aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID())
+    if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+                 aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID())) {
       aCoincidences.insert(aRef);
+      AttributeRefAttrPtr aRefAttrA = aRef->refattr(SketchPlugin_Constraint::ENTITY_A());
+      if (!aRefAttrA->isObject())
+        aCoincidentPoints[aRefAttrA->attr()] = aRef;
+      AttributeRefAttrPtr aRefAttrB = aRef->refattr(SketchPlugin_Constraint::ENTITY_B());
+      if (!aRefAttrB->isObject())
+        aCoincidentPoints[aRefAttrB->attr()] = aRef;
+    }
   }
 
   // leave only coincidences referred to the second feature
@@ -317,6 +400,20 @@ std::set<FeaturePtr> collectCoincidences(FeaturePtr theFeature1, FeaturePtr theF
     FeaturePtr aRef = ModelAPI_Feature::feature((*anIt)->owner());
     if (aCoincidences.find(aRef) != aCoincidences.end())
       aCoincidencesBetweenFeatures.insert(aRef);
+    else if (aRef && (aRef->getKind() == SketchPlugin_ConstraintCoincidence::ID() ||
+                      aRef->getKind() == SketchPlugin_ConstraintCoincidenceInternal::ID())) {
+      for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+        AttributeRefAttrPtr aRefAttr = aRef->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+        if (aRefAttr && !aRefAttr->isObject()) {
+          std::map<AttributePtr, FeaturePtr>::iterator aFound =
+              aCoincidentPoints.find(aRefAttr->attr());
+          if (aFound != aCoincidentPoints.end()) {
+            aCoincidencesBetweenFeatures.insert(aRef);
+            aCoincidencesBetweenFeatures.insert(aFound->second);
+          }
+        }
+      }
+    }
   }
 
   return aCoincidencesBetweenFeatures;
@@ -329,20 +426,31 @@ std::set<AttributePtr> coincidentBoundaryPoints(FeaturePtr theFeature1, FeatureP
   std::set<AttributePtr> aCoincidentPoints;
   std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
   for (; aCIt != aCoincidences.end(); ++ aCIt) {
-    AttributeRefAttrPtr aRefAttrA = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_A());
-    AttributeRefAttrPtr aRefAttrB = (*aCIt)->refattr(SketchPlugin_Constraint::ENTITY_B());
-    if (!aRefAttrA || aRefAttrA->isObject() ||
-        !aRefAttrB || aRefAttrB->isObject())
-      continue;
-
-    AttributePtr anAttrA = aRefAttrA->attr();
-    AttributePtr anAttrB = aRefAttrB->attr();
-    if (anAttrA->id() != SketchPlugin_Arc::CENTER_ID() &&
-        anAttrA->id() != SketchPlugin_Circle::CENTER_ID() &&
-        anAttrB->id() != SketchPlugin_Arc::CENTER_ID() &&
-        anAttrB->id() != SketchPlugin_Circle::CENTER_ID()) {
-      aCoincidentPoints.insert(anAttrA);
-      aCoincidentPoints.insert(anAttrB);
+    for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
+      AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
+      if (!aRefAttr || aRefAttr->isObject())
+        continue;
+
+      AttributePtr anAttr = aRefAttr->attr();
+      FeaturePtr anOwner = ModelAPI_Feature::feature(anAttr->owner());
+      if (anOwner == theFeature1 || anOwner == theFeature2) {
+        if (anAttr->id() == SketchPlugin_BSplineBase::POLES_ID()) {
+          AttributeIntegerPtr anIndex = (*aCIt)->integer(i == 0 ?
+              SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A() :
+              SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
+          if (anIndex) {
+            if (anIndex->value() == 0)
+              anAttr = anOwner->attribute(SketchPlugin_BSpline::START_ID());
+            else
+              anAttr = anOwner->attribute(SketchPlugin_BSpline::END_ID());
+            if (anAttr)
+              aCoincidentPoints.insert(anAttr);
+          }
+        }
+        else if (anAttr->id() != SketchPlugin_Arc::CENTER_ID() &&
+                 anAttr->id() != SketchPlugin_Circle::CENTER_ID())
+          aCoincidentPoints.insert(anAttr);
+      }
     }
   }
   return aCoincidentPoints;
index 57424b293492e0284afa606dc63c178c4499d1c7..1469eced5e8068b1965cc2e5985e00462e9b0e58 100644 (file)
@@ -40,6 +40,11 @@ public:
   virtual void notify(const FeaturePtr&      theFeature,
                       PlaneGCSSolver_Update* theUpdater);
 
+  /// \brief Tries to remove constraint
+  /// \return \c false, if current constraint contains another SketchPlugin constraints
+  /// (like for multiple coincidence)
+  virtual bool remove();
+
 protected:
   /// \brief Converts SketchPlugin constraint to a list of solver constraints
   virtual void process();
@@ -56,6 +61,7 @@ private:
   double myCurveCurveAngle;
   AttributePtr mySharedPoint;
   EntityWrapperPtr myAuxPoint;
+  ScalarWrapperPtr myAuxParameters[2];
 };
 
 #endif
index 96c0a29687a66e1008520dd4d398e5244df9ce18..4f1f5ee863388733c7b2e335ac606d1e2316c4a5 100644 (file)
@@ -164,10 +164,11 @@ static SolverConstraintPtr move(StoragePtr theStorage,
                                 int theSketchDOF,
                                 bool theEventsBlocked,
                                 Type theFeatureOrPoint,
+                                const EntityWrapperPtr& theSolverEntity,
                                 const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
                                 const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
 {
-  bool isEntityExists = (theStorage->entity(theFeatureOrPoint).get() != 0);
+  bool isEntityExists = (theSolverEntity.get() != 0);
   if (theSketchDOF == 0 && isEntityExists) {
     // avoid moving elements of fully constrained sketch
     theStorage->refresh();
@@ -196,18 +197,29 @@ bool SketchSolver_Group::moveFeature(FeaturePtr theFeature,
                                      const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
                                      const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
 {
-  SolverConstraintPtr aConstraint =
-      move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theFeature, theFrom, theTo);
+  EntityWrapperPtr anEntity = myStorage->entity(theFeature);
+  SolverConstraintPtr aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked,
+                                         theFeature, anEntity, theFrom, theTo);
   setTemporary(aConstraint);
   return true;
 }
 
 bool SketchSolver_Group::movePoint(AttributePtr theAttribute,
+                                   const int thePointIndex,
                                    const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
                                    const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
 {
-  SolverConstraintPtr aConstraint =
-      move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked, theAttribute, theFrom, theTo);
+  EntityWrapperPtr anEntity = myStorage->entity(theAttribute);
+  SolverConstraintPtr aConstraint;
+  if (thePointIndex < 0) {
+    aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked,
+                       theAttribute, anEntity, theFrom, theTo);
+  }
+  else {
+    aConstraint = move(myStorage, mySketchSolver, myDOF, myIsEventsBlocked,
+                       std::pair<AttributePtr, int>(theAttribute, thePointIndex), anEntity,
+                       theFrom, theTo);
+  }
   setTemporary(aConstraint);
   return true;
 }
index 45407ddb8f47884f6dbc4efb5a4ede2d0a567a25..d5617155932ec56e073b7ed706b172f2641e7edf 100644 (file)
@@ -90,12 +90,14 @@ class SketchSolver_Group
                    const std::shared_ptr<GeomAPI_Pnt2d>& theTo);
   /** \brief Updates the data corresponding the specified point moved in GUI.
    *         Special kind of Fixed constraints is created.
-   *  \param[in] thePoint the attribute to be updated
-   *  \param[in] theFrom  start point of the movement
-   *  \param[in] theTo    final point of the movement
+   *  \param[in] thePointOrArray the attribute to be updated
+   *  \param[in] thePointIndex   index of moved point in array
+   *  \param[in] theFrom         start point of the movement
+   *  \param[in] theTo           destination point of the movement
    *  \return \c true, if the attribute is really moved
    */
-  bool movePoint(AttributePtr thePoint,
+  bool movePoint(AttributePtr thePointOrArray,
+                 const int thePointIndex,
                  const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
                  const std::shared_ptr<GeomAPI_Pnt2d>& theTo);
 
index a9d47533e95b12fb5cb18ad2dc632add3e8c13a5..b7b6d498036f4a8f64579d55cf8f56fb57eea235 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <Events_Loop.h>
 #include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
 #include <ModelAPI_Events.h>
 #include <ModelAPI_ResultConstruction.h>
 #include <ModelAPI_Session.h>
@@ -87,6 +88,19 @@ static void featuresOrderedByType(const std::set<ObjectPtr>& theOriginalFeatures
   }
 }
 
+static void setPoint(AttributePtr theAttribute,
+                     const int thePointIndex,
+                     const std::shared_ptr<GeomAPI_Pnt2d> theValue)
+{
+  AttributePoint2DPtr aPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(theAttribute);
+  AttributePoint2DArrayPtr aPointArrayAttr =
+      std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(theAttribute);
+  if (aPointAttr)
+    aPointAttr->setValue(theValue);
+  else if (aPointArrayAttr && thePointIndex >= 0)
+    aPointArrayAttr->setPnt(thePointIndex, theValue);
+}
+
 
 
 // ========================================================
@@ -180,8 +194,8 @@ void SketchSolver_Manager::processEvent(
         std::dynamic_pointer_cast<ModelAPI_ObjectMovedMessage>(theMessage);
 
     ObjectPtr aMovedObject = aMoveMsg->movedObject();
-    std::shared_ptr<GeomDataAPI_Point2D> aMovedPoint =
-        std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aMoveMsg->movedAttribute());
+    AttributePtr aMovedAttribute = aMoveMsg->movedAttribute();
+    int aMovedPoint = aMoveMsg->movedPointIndex();
 
     const std::shared_ptr<GeomAPI_Pnt2d>& aFrom = aMoveMsg->originalPosition();
     const std::shared_ptr<GeomAPI_Pnt2d>& aTo = aMoveMsg->currentPosition();
@@ -192,8 +206,8 @@ void SketchSolver_Manager::processEvent(
           std::dynamic_pointer_cast<SketchPlugin_Feature>(aMovedFeature);
       if (aSketchFeature && !aSketchFeature->isMacro())
         needToResolve = moveFeature(aSketchFeature, aFrom, aTo);
-    } else if (aMovedPoint)
-      needToResolve = moveAttribute(aMovedPoint, aFrom, aTo);
+    } else if (aMovedAttribute)
+      needToResolve = moveAttribute(aMovedAttribute, aMovedPoint, aFrom, aTo);
 
   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aDeleteMsg =
@@ -349,7 +363,8 @@ bool SketchSolver_Manager::moveFeature(
 //  Purpose:  move given attribute in appropriate group
 // ============================================================================
 bool SketchSolver_Manager::moveAttribute(
-    const std::shared_ptr<GeomDataAPI_Point2D>& theMovedAttribute,
+    const std::shared_ptr<ModelAPI_Attribute>& theMovedAttribute,
+    const int theMovedPointIndex,
     const std::shared_ptr<GeomAPI_Pnt2d>& theFrom,
     const std::shared_ptr<GeomAPI_Pnt2d>& theTo)
 {
@@ -358,7 +373,7 @@ bool SketchSolver_Manager::moveAttribute(
       std::dynamic_pointer_cast<SketchPlugin_Constraint>(anOwner);
   if (aConstraint)
   {
-    theMovedAttribute->setValue(theTo);
+    setPoint(theMovedAttribute, theMovedPointIndex, theTo);
     Events_Loop::loop()->flush(Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
     return true;
   }
@@ -369,12 +384,12 @@ bool SketchSolver_Manager::moveAttribute(
   if (aSketchFeature)
     aGroup = findGroup(aSketchFeature);
   if (!aGroup) {
-    theMovedAttribute->setValue(theTo);
+    setPoint(theMovedAttribute, theMovedPointIndex, theTo);
     return false;
   }
 
   aGroup->blockEvents(true);
-  return aGroup->movePoint(theMovedAttribute, theFrom, theTo);
+  return aGroup->movePoint(theMovedAttribute, theMovedPointIndex, theFrom, theTo);
 }
 
 // ============================================================================
index 78ddaefbb26c2d99811bb51d0462d4564519ace8..33aa152a513fefc54e4ab734f9afc366505396f2 100644 (file)
@@ -85,12 +85,14 @@ protected:
                    const std::shared_ptr<GeomAPI_Pnt2d>& theToPoint);
 
   /** \brief Move feature using its moved attribute
-   *  \param[in] theMovedAttribute dragged point attribute of sketch feature
-   *  \param[in] theFromPoint      original position of the moved point
-   *  \param[in] theToPoint        prefereble position (current position of the mouse)
+   *  \param[in] theMovedAttribute  dragged point (array of points) attribute of sketch feature
+   *  \param[in] theMovedPointIndex index of dragged point in an array (-1 otherwise)
+   *  \param[in] theFromPoint       original position of the moved point
+   *  \param[in] theToPoint         prefereble position (current position of the mouse)
    *  \return \c true if the attribute owner has been changed successfully
    */
-  bool moveAttribute(const std::shared_ptr<GeomDataAPI_Point2D>& theMovedAttribute,
+  bool moveAttribute(const std::shared_ptr<ModelAPI_Attribute>& theMovedAttribute,
+                     const int theMovedPointIndex,
                      const std::shared_ptr<GeomAPI_Pnt2d>& theFromPoint,
                      const std::shared_ptr<GeomAPI_Pnt2d>& theToPoint);
 
index f84bb43220f1cee2fcf8334c8779f1ea818ca7f6..51a7b9f8be1b09954e5ed83fe2e5070ac06bc586 100644 (file)
@@ -26,7 +26,7 @@ SET(PROJECT_HEADERS
     SketcherPrs.h
     SketcherPrs_Coincident.h
     SketcherPrs_Collinear.h
-    SketcherPrs_DimensionStyleListener.h
+    SketcherPrs_DimensionStyle.h
     SketcherPrs_Factory.h
     SketcherPrs_Parallel.h
     SketcherPrs_Tools.h
@@ -49,7 +49,7 @@ SET(PROJECT_HEADERS
 SET(PROJECT_SOURCES
     SketcherPrs_Coincident.cpp
     SketcherPrs_Collinear.cpp
-    SketcherPrs_DimensionStyleListener.cpp
+    SketcherPrs_DimensionStyle.cpp
     SketcherPrs_Factory.cpp
     SketcherPrs_Parallel.cpp
     SketcherPrs_Tools.cpp
index 1696fc7e1aadd317fff8be6e6faf734f362e17d8..f09c4a9331f45f430eda3efc2cf4151f8d24b85b 100644 (file)
@@ -18,7 +18,7 @@
 //
 
 #include "SketcherPrs_Angle.h"
-#include "SketcherPrs_DimensionStyleListener.h"
+#include "SketcherPrs_DimensionStyle.h"
 #include "SketcherPrs_Tools.h"
 
 #include <SketchPlugin_ConstraintAngle.h>
@@ -70,7 +70,7 @@ SketcherPrs_Angle::SketcherPrs_Angle(ModelAPI_Feature* theConstraint,
 
   SetDimensionAspect(myAspect);
 
-  myStyleListener = new SketcherPrs_DimensionStyleListener();
+  myStyleListener = new SketcherPrs_DimensionStyle();
 }
 
 SketcherPrs_Angle::~SketcherPrs_Angle()
@@ -91,6 +91,8 @@ bool SketcherPrs_Angle::readyToDisplay(ModelAPI_Feature* theConstraint,
                                        gp_Pnt& theCenterPoint)
 {
   bool aReadyToDisplay = false;
+  if (!thePlane)
+    return aReadyToDisplay;
 
   DataPtr aData = theConstraint->data();
 
@@ -141,6 +143,11 @@ bool SketcherPrs_Angle::readyToDisplay(ModelAPI_Feature* theConstraint,
     std::shared_ptr<GeomAPI_Lin2d> aLine2(new GeomAPI_Lin2d(aStartB->pnt(), aEndB->pnt()));
     bool isReversed2 =
       aData->boolean(SketchPlugin_ConstraintAngle::ANGLE_REVERSED_SECOND_LINE_ID())->value();
+
+    if (aData->integer(SketchPlugin_ConstraintAngle::TYPE_ID())->value() ==
+        (int)SketcherPrs_Tools::ANGLE_COMPLEMENTARY)
+      isReversed1 = !isReversed1;
+
     anAng = std::shared_ptr<GeomAPI_Angle2d>(
       new GeomAPI_Angle2d(aLine1, isReversed1, aLine2, isReversed2));
   }
@@ -156,6 +163,9 @@ bool SketcherPrs_Angle::readyToDisplay(ModelAPI_Feature* theConstraint,
     aSecondPoint = anAng->firstPoint()->impl<gp_Pnt2d>();
   }
 
+  if (aData->integer(SketchPlugin_ConstraintAngle::TYPE_ID())->value() ==
+      (int)SketcherPrs_Tools::ANGLE_BACKWARD)
+    std::swap(aFirstPoint, aSecondPoint);
 
   std::shared_ptr<GeomAPI_Pnt> aPoint = thePlane->to3D(aFirstPoint.X(), aFirstPoint.Y());
   theFirstPoint = aPoint->impl<gp_Pnt>();
@@ -199,48 +209,15 @@ void SketcherPrs_Angle::Compute(const Handle(PrsMgr_PresentationManager3d)& theP
     myFlyOutPoint = aFlyoutPnt->impl<gp_Pnt>();
   }
 
-  std::shared_ptr<ModelAPI_AttributeInteger> aTypeAttr = std::dynamic_pointer_cast<
-      ModelAPI_AttributeInteger>(aData->attribute(SketchPlugin_ConstraintAngle::TYPE_ID()));
-  SketcherPrs_Tools::AngleType anAngleType = (SketcherPrs_Tools::AngleType)(aTypeAttr->value());
-
   double aDist = -1;
-  switch (anAngleType) {
-    case SketcherPrs_Tools::ANGLE_DIRECT: {
-#ifndef COMPILATION_CORRECTION
-      SetArrowsVisibility(AIS_TOAV_Both);
-#endif
-      SetMeasuredGeometry(myFirstPoint, myCenterPoint, mySecondPoint);
-#ifndef COMPILATION_CORRECTION
-      bool isReversedPlanes = isAnglePlaneReversedToSketchPlane();
-      SetType(!isReversedPlanes ? AIS_TOA_Exterior : AIS_TOA_Interior);
-#endif
-    }
-    break;
-    case SketcherPrs_Tools::ANGLE_COMPLEMENTARY: {
-      double anEdge1Length = aCenterPoint.Distance(myFirstPoint);
-      //aDist = calculateDistanceToFlyoutPoint();
-      gp_Pnt aFirstPoint = aCenterPoint.Translated(
-                          gp_Vec(myCenterPoint, myFirstPoint).Normalized() * (-anEdge1Length));
-      SetMeasuredGeometry(aFirstPoint, myCenterPoint, mySecondPoint);
 #ifndef COMPILATION_CORRECTION
-      SetType(AIS_TOA_Interior);
+  SetArrowsVisibility(AIS_TOAV_Both);
 #endif
-    }
-    break;
-    case SketcherPrs_Tools::ANGLE_BACKWARD: {
+  SetMeasuredGeometry(myFirstPoint, myCenterPoint, mySecondPoint);
 #ifndef COMPILATION_CORRECTION
-      SetArrowsVisibility(AIS_TOAV_Both);
+  bool isReversedPlanes = isAnglePlaneReversedToSketchPlane();
+  SetType(!isReversedPlanes ? AIS_TOA_Exterior : AIS_TOA_Interior);
 #endif
-      SetMeasuredGeometry(myFirstPoint, myCenterPoint, mySecondPoint);
-      bool isReversedPlanes = isAnglePlaneReversedToSketchPlane();
-#ifndef COMPILATION_CORRECTION
-      SetType(isReversedPlanes ? AIS_TOA_Exterior : AIS_TOA_Interior);
-#endif
-    }
-    break;
-    default:
-      break;
-  }
   if (aDist < 0) /// it was not calculated yet
     aDist = calculateDistanceToFlyoutPoint();
   SetFlyout(aDist);
index 75d0c37d81888c8d869d1555128ace51335dedc0..009a482a8c353ff1b910b677f481a2c555ad78c9 100644 (file)
@@ -27,7 +27,7 @@
 #include <AIS_AngleDimension.hxx>
 #include <Standard_DefineHandle.hxx>
 
-#include <SketcherPrs_DimensionStyleListener.h>
+#include <SketcherPrs_DimensionStyle.h>
 
 DEFINE_STANDARD_HANDLE(SketcherPrs_Angle, AIS_AngleDimension)
 
@@ -99,7 +99,7 @@ private:
   Handle(Prs3d_DimensionAspect) myAspect;
 
   /// Listener to update dimension visualization style
-  SketcherPrs_DimensionStyleListener* myStyleListener;
+  SketcherPrs_DimensionStyle* myStyleListener;
 
   /// container of values obtained from the constraint, which are necessary to fill the presentation
   gp_Pnt myFirstPoint; ///< the dimension first point for measured geometry
@@ -107,7 +107,7 @@ private:
   gp_Pnt myCenterPoint; ///< the dimension center point for measured geometry
   gp_Pnt myFlyOutPoint;  ///< the dimension fly out point for measured geometry
 
-  SketcherPrs_DimensionStyleListener::DimensionValue myValue; /// the structure filled by constraint
+  SketcherPrs_DimensionStyle::DimensionValue myValue; /// the structure filled by constraint
 };
 
 
diff --git a/src/SketcherPrs/SketcherPrs_DimensionStyle.cpp b/src/SketcherPrs/SketcherPrs_DimensionStyle.cpp
new file mode 100644 (file)
index 0000000..fb82eb4
--- /dev/null
@@ -0,0 +1,117 @@
+// 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 "SketcherPrs_DimensionStyle.h"
+#include <Prs3d_DimensionAspect.hxx>
+#include "SketcherPrs_Tools.h"
+
+#include <Events_Loop.h>
+
+#include <AIS_Dimension.hxx>
+#include <TCollection_ExtendedString.hxx>
+
+//#ifndef WNT
+//  #define COMPILATION_CORRECTION
+//#endif
+
+// it is not possible to use 0x2211 as summ symbol because it is not supported by
+// debian Linux platform
+static const Standard_ExtCharacter MyEmptySymbol(' ');
+static const Standard_ExtCharacter MySigmaSymbol('=');//0x03A3); // using equal instead of sigma
+
+
+SketcherPrs_DimensionStyle::DimensionValue::DimensionValue(double theDoubleValue,
+                                     bool theHasParameters, const std::string& theTextValue)
+: myDoubleValue(theDoubleValue),
+  myHasParameters(theHasParameters),
+  myTextValue(theTextValue)
+{
+}
+
+void SketcherPrs_DimensionStyle::DimensionValue::init(
+                                                const AttributeDoublePtr& theAttributeValue)
+{
+  myDoubleValue = theAttributeValue->value();
+  myHasParameters = theAttributeValue->usedParameters().size() > 0;
+  myTextValue = theAttributeValue->text();
+}
+
+SketcherPrs_DimensionStyle::SketcherPrs_DimensionStyle()
+{
+}
+
+SketcherPrs_DimensionStyle::~SketcherPrs_DimensionStyle()
+{
+}
+
+void SketcherPrs_DimensionStyle::updateDimensions(AIS_Dimension* theDimension,
+          const SketcherPrs_DimensionStyle::DimensionValue& theDimensionValue)
+{
+  if (!theDimension)
+    return;
+  updateDimensions(theDimension, theDimensionValue.myHasParameters,
+                   theDimensionValue.myTextValue, theDimensionValue.myDoubleValue);
+}
+
+void SketcherPrs_DimensionStyle::updateDimensions(AIS_Dimension* theDimension,
+                                                          const bool theHasParameters,
+                                                          const std::string& theTextValue,
+                                                          const double theDoubleValue)
+{
+  if (!theDimension)
+    return;
+
+  /// do not show special symbols of dimension:
+  ///   previous implementation did not allow to unite them
+  theDimension->SetSpecialSymbol(MyEmptySymbol);
+  theDimension->SetDisplaySpecialSymbol(AIS_DSS_No);
+
+  TCollection_ExtendedString aCustomValue;
+  if (theHasParameters) {
+    //bool isParameterTextStyle = myStyle == SketcherPrs_ParameterStyleMessage::ParameterText;
+    bool isParameterTextStyle =
+      SketcherPrs_Tools::parameterStyle() == SketcherPrs_Tools::ParameterText;
+
+    if (isParameterTextStyle)
+      aCustomValue = theTextValue.c_str();
+    else {
+      // format value string using "sprintf"
+      TCollection_AsciiString aFormatStr =
+        theDimension->Attributes()->DimensionAspect()->ValueStringFormat();
+      char aFmtBuffer[256];
+      sprintf (aFmtBuffer, aFormatStr.ToCString(), theDoubleValue);
+      aCustomValue = TCollection_ExtendedString (aFmtBuffer);
+
+      aCustomValue.Insert (1, MySigmaSymbol);
+    }
+  }
+  else {
+    // format value string using "sprintf"
+    TCollection_AsciiString aFormatStr =
+      theDimension->Attributes()->DimensionAspect()->ValueStringFormat();
+    char aFmtBuffer[256];
+    sprintf (aFmtBuffer, aFormatStr.ToCString(), theDoubleValue);
+    aCustomValue = TCollection_ExtendedString (aFmtBuffer);
+  }
+#ifdef COMPILATION_CORRECTION
+  theDimension->SetCustomValue(theDoubleValue);
+#else
+  theDimension->SetCustomValue(aCustomValue);
+#endif
+}
diff --git a/src/SketcherPrs/SketcherPrs_DimensionStyle.h b/src/SketcherPrs/SketcherPrs_DimensionStyle.h
new file mode 100644 (file)
index 0000000..47be050
--- /dev/null
@@ -0,0 +1,80 @@
+// 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 SketcherPrs_DimensionStyleListener_H
+#define SketcherPrs_DimensionStyleListener_H
+
+//#include <Events_Listener.h>
+
+#include <ModelAPI_AttributeDouble.h>
+
+#include "SketcherPrs_Tools.h"
+
+#include <Standard.hxx>
+
+#include <string>
+
+/**
+* \class SketcherPrs_DimensionStyleListener
+* \ingroup GUI
+* A class for representation of linear dimension constraint.
+* It supports SketchPlugin_ConstraintLength and SketchPlugin_ConstraintDistance features.
+*/
+class SketcherPrs_DimensionStyle
+{
+public:
+  class DimensionValue {
+  public:
+    DimensionValue(double theDoubleValue, bool theHasParameters, const std::string& theTextValue);
+    /// Fills internal fields by the given attribute
+    /// \param theAttributeValue a model attribute
+    void init(const AttributeDoublePtr& theAttributeValue);
+
+  public:
+    double myDoubleValue; ///< the angle value to be shown as custom value of presentation
+    bool myHasParameters; ///< true if the atrribute value has used parameters
+    std::string myTextValue; ///< the angle value depending on angle type
+  };
+
+public:
+  /// Constructor
+  Standard_EXPORT SketcherPrs_DimensionStyle();
+
+  /// Destructor
+  Standard_EXPORT ~SketcherPrs_DimensionStyle();
+
+  /// Visualizes the dimension text or dimension value depending on the has parameters state
+  /// \param theDimension a modified dimension
+  /// \param theDimensionValue container filled by the model double attribute
+  Standard_EXPORT void updateDimensions(AIS_Dimension* theDimension,
+                                        const DimensionValue& theDimensionValue);
+
+private:
+  /// Visualizes the dimension text or dimension value depending on the has parameters state
+  /// \param theDimension a modified dimension
+  /// \param theHasParameters if true, the text is shown, else digit
+  /// \param theTextValue a dimension text value
+  /// \param theDoubleValue a dimension digit value
+  void updateDimensions(AIS_Dimension* theDimension,
+                        const bool theHasParameters,
+                        const std::string& theTextValue,
+                        const double theDoubleValue);
+};
+
+#endif
\ No newline at end of file
diff --git a/src/SketcherPrs/SketcherPrs_DimensionStyleListener.cpp b/src/SketcherPrs/SketcherPrs_DimensionStyleListener.cpp
deleted file mode 100644 (file)
index e00f81a..0000000
+++ /dev/null
@@ -1,131 +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 "SketcherPrs_DimensionStyleListener.h"
-#include <Prs3d_DimensionAspect.hxx>
-#include "SketcherPrs_Tools.h"
-
-#include <Events_Loop.h>
-
-#include <AIS_Dimension.hxx>
-#include <TCollection_ExtendedString.hxx>
-
-//#ifndef WNT
-//  #define COMPILATION_CORRECTION
-//#endif
-
-// it is not possible to use 0x2211 as summ symbol because it is not supported by
-// debian Linux platform
-static const Standard_ExtCharacter MyEmptySymbol(' ');
-static const Standard_ExtCharacter MySigmaSymbol('=');//0x03A3); // using equal instead of sigma
-
-SketcherPrs_DimensionStyleListener::DimensionValue::DimensionValue(double theDoubleValue,
-                                     bool theHasParameters, const std::string& theTextValue)
-: myDoubleValue(theDoubleValue),
-  myHasParameters(theHasParameters),
-  myTextValue(theTextValue)
-{
-}
-
-void SketcherPrs_DimensionStyleListener::DimensionValue::init(
-                                                const AttributeDoublePtr& theAttributeValue)
-{
-  myDoubleValue = theAttributeValue->value();
-  myHasParameters = theAttributeValue->usedParameters().size() > 0;
-  myTextValue = theAttributeValue->text();
-}
-
-SketcherPrs_DimensionStyleListener::SketcherPrs_DimensionStyleListener()
-{
-  Events_Loop* aLoop = Events_Loop::loop();
-  const Events_ID kDocCreatedEvent =
-                SketcherPrs_ParameterStyleMessage::eventId();
-  aLoop->registerListener(this, kDocCreatedEvent, NULL, false);
-}
-
-SketcherPrs_DimensionStyleListener::~SketcherPrs_DimensionStyleListener()
-{
-  Events_Loop* aLoop = Events_Loop::loop();
-  aLoop->removeListener(this);
-}
-
-void SketcherPrs_DimensionStyleListener::processEvent(
-  const std::shared_ptr<Events_Message>& theMessage)
-{
-  const Events_ID kParameterStyleEvent = SketcherPrs_ParameterStyleMessage::eventId();
-  if (theMessage->eventID() == kParameterStyleEvent) {
-    std::shared_ptr<SketcherPrs_ParameterStyleMessage> aMessage = std::dynamic_pointer_cast<
-                                            SketcherPrs_ParameterStyleMessage>(theMessage);
-    myStyle = aMessage->style();
-  }
-}
-
-void SketcherPrs_DimensionStyleListener::updateDimensions(AIS_Dimension* theDimension,
-          const SketcherPrs_DimensionStyleListener::DimensionValue& theDimensionValue)
-{
-  if (!theDimension)
-    return;
-  updateDimensions(theDimension, theDimensionValue.myHasParameters,
-                   theDimensionValue.myTextValue, theDimensionValue.myDoubleValue);
-}
-
-void SketcherPrs_DimensionStyleListener::updateDimensions(AIS_Dimension* theDimension,
-                                                          const bool theHasParameters,
-                                                          const std::string& theTextValue,
-                                                          const double theDoubleValue)
-{
-  if (!theDimension)
-    return;
-
-  /// do not show special symbols of dimension: previous implementation did not allow to unite them
-  theDimension->SetSpecialSymbol(MyEmptySymbol);
-  theDimension->SetDisplaySpecialSymbol(AIS_DSS_No);
-
-  TCollection_ExtendedString aCustomValue;
-  if (theHasParameters) {
-    bool isParameterTextStyle = myStyle == SketcherPrs_ParameterStyleMessage::ParameterText;
-
-    if (isParameterTextStyle)
-      aCustomValue = theTextValue.c_str();
-    else {
-      // format value string using "sprintf"
-      TCollection_AsciiString aFormatStr =
-        theDimension->Attributes()->DimensionAspect()->ValueStringFormat();
-      char aFmtBuffer[256];
-      sprintf (aFmtBuffer, aFormatStr.ToCString(), theDoubleValue);
-      aCustomValue = TCollection_ExtendedString (aFmtBuffer);
-
-      aCustomValue.Insert (1, MySigmaSymbol);
-    }
-  }
-  else {
-    // format value string using "sprintf"
-    TCollection_AsciiString aFormatStr =
-      theDimension->Attributes()->DimensionAspect()->ValueStringFormat();
-    char aFmtBuffer[256];
-    sprintf (aFmtBuffer, aFormatStr.ToCString(), theDoubleValue);
-    aCustomValue = TCollection_ExtendedString (aFmtBuffer);
-  }
-#ifdef COMPILATION_CORRECTION
-  theDimension->SetCustomValue(theDoubleValue);
-#else
-  theDimension->SetCustomValue(aCustomValue);
-#endif
-}
-
diff --git a/src/SketcherPrs/SketcherPrs_DimensionStyleListener.h b/src/SketcherPrs/SketcherPrs_DimensionStyleListener.h
deleted file mode 100644 (file)
index 5e62399..0000000
+++ /dev/null
@@ -1,87 +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 SketcherPrs_DimensionStyleListener_H
-#define SketcherPrs_DimensionStyleListener_H
-
-#include <Events_Listener.h>
-
-#include <ModelAPI_AttributeDouble.h>
-
-#include "SketcherPrs_Tools.h"
-
-#include <Standard.hxx>
-
-#include <string>
-
-/**
-* \class SketcherPrs_DimensionStyleListener
-* \ingroup GUI
-* A class for representation of linear dimension constraint.
-* It supports SketchPlugin_ConstraintLength and SketchPlugin_ConstraintDistance features.
-*/
-class SketcherPrs_DimensionStyleListener : public Events_Listener
-{
-public:
-  class DimensionValue {
-  public:
-    DimensionValue(double theDoubleValue, bool theHasParameters, const std::string& theTextValue);
-    /// Fills internal fields by the given attribute
-    /// \param theAttributeValue a model attribute
-    void init(const AttributeDoublePtr& theAttributeValue);
-
-  public:
-    double myDoubleValue; ///< the angle value to be shown as custom value of presentation
-    bool myHasParameters; ///< true if the atrribute value has used parameters
-    std::string myTextValue; ///< the angle value depending on angle type
-  };
-
-public:
-  /// Constructor
-  Standard_EXPORT SketcherPrs_DimensionStyleListener();
-
-  /// Destructor
-  Standard_EXPORT ~SketcherPrs_DimensionStyleListener();
-
-  /// Process the ModelAPI_DocumentCreatedMessage to fulfill a document
-  /// from the message with origin and planes
-  virtual void processEvent(const std::shared_ptr<Events_Message>& theMessage);
-
-  /// Visualizes the dimension text or dimension value depending on the has parameters state
-  /// \param theDimension a modified dimension
-  /// \param theDimensionValue container filled by the model double attribute
-  Standard_EXPORT void updateDimensions(AIS_Dimension* theDimension,
-                                        const DimensionValue& theDimensionValue);
-
-private:
-  /// Visualizes the dimension text or dimension value depending on the has parameters state
-  /// \param theDimension a modified dimension
-  /// \param theHasParameters if true, the text is shown, else digit
-  /// \param theTextValue a dimension text value
-  /// \param theDoubleValue a dimension digit value
-  void updateDimensions(AIS_Dimension* theDimension,
-                        const bool theHasParameters,
-                        const std::string& theTextValue,
-                        const double theDoubleValue);
-private:
-  /// Style how the parameter of dimension should be visualized
-  SketcherPrs_ParameterStyleMessage::ParameterStyle myStyle;
-};
-
-#endif
\ No newline at end of file
index b15f0d05f0459ece2a964e17187769c4d103cbcb..de9d7e7520178e039c1b7e6605d0d9c400d320b3 100644 (file)
@@ -19,7 +19,7 @@
 
 #include "SketcherPrs_LengthDimension.h"
 #include "SketcherPrs_Tools.h"
-#include "SketcherPrs_DimensionStyleListener.h"
+#include "SketcherPrs_DimensionStyle.h"
 
 #include <SketchPlugin_Constraint.h>
 #include <SketchPlugin_ConstraintLength.h>
@@ -120,7 +120,7 @@ SketcherPrs_LengthDimension::SketcherPrs_LengthDimension(ModelAPI_Feature* theCo
   myValue(0., false, "")
 {
   SetDimensionAspect(createDimensionAspect());
-  myStyleListener = new SketcherPrs_DimensionStyleListener();
+  myStyleListener = new SketcherPrs_DimensionStyle();
 
   setDirection(theConstraint, plane());
 }
index 43a27c71787e7f746145e5b38af1c86f395a7048..b737488fa566497d95df49b744fd62d7f9278c3c 100644 (file)
@@ -30,7 +30,7 @@
 #include <gp_Pln.hxx>
 #include <string>
 
-#include <SketcherPrs_DimensionStyleListener.h>
+#include <SketcherPrs_DimensionStyle.h>
 
 DEFINE_STANDARD_HANDLE(SketcherPrs_LengthDimension, AIS_LengthDimension)
 
@@ -96,7 +96,7 @@ private:
   //Handle(Prs3d_DimensionAspect) myAspect;
 
   /// Listener to update dimension visualization style
-  SketcherPrs_DimensionStyleListener* myStyleListener;
+  SketcherPrs_DimensionStyle* myStyleListener;
 
   /// container of values obtained from the constraint, which are necessary
   /// to fill the presentation
@@ -106,7 +106,7 @@ private:
   double myDistance; ///< the flyout distance
 
   /// the structure filled by constraint
-  SketcherPrs_DimensionStyleListener::DimensionValue myValue;
+  SketcherPrs_DimensionStyle::DimensionValue myValue;
 };
 
 #endif
\ No newline at end of file
index bd10874fcd469eca67559649a633c3767eaea594..5b94a63de3db61aafd2a09326ef0baa3468c4c2b 100644 (file)
@@ -19,7 +19,7 @@
 
 #include "SketcherPrs_Radius.h"
 #include "SketcherPrs_Tools.h"
-#include "SketcherPrs_DimensionStyleListener.h"
+#include "SketcherPrs_DimensionStyle.h"
 
 #include <SketchPlugin_ConstraintRadius.h>
 #include <SketchPlugin_Constraint.h>
@@ -59,7 +59,7 @@ SketcherPrs_Radius::SketcherPrs_Radius(ModelAPI_Feature* theConstraint,
   myValue(1, false, "")
 {
   SetDimensionAspect(createDimensionAspect());
-  myStyleListener = new SketcherPrs_DimensionStyleListener();
+  myStyleListener = new SketcherPrs_DimensionStyle();
 }
 
 SketcherPrs_Radius::~SketcherPrs_Radius()
index a6fbe75e9227e91fac4e1954c7c8eafe368661e4..e675e24e0a3836ace77b5cea6d513282928395a6 100644 (file)
@@ -27,7 +27,7 @@
 #include <AIS_RadiusDimension.hxx>
 #include <Standard_DefineHandle.hxx>
 
-#include <SketcherPrs_DimensionStyleListener.h>
+#include <SketcherPrs_DimensionStyle.h>
 
 DEFINE_STANDARD_HANDLE(SketcherPrs_Radius, AIS_RadiusDimension)
 
@@ -91,13 +91,13 @@ private:
   SketchPlugin_Sketch* mySketcher;
 
   /// Listener to update dimension visualization style
-  SketcherPrs_DimensionStyleListener* myStyleListener;
+  SketcherPrs_DimensionStyle* myStyleListener;
 
   /// container of values obtained from the constraint, which are necessary to fill the presentation
   gp_Circ myCircle; ///< the radius circle
   gp_Pnt myAnchorPoint; ///< an ancor for the radius value visualization
 
-  SketcherPrs_DimensionStyleListener::DimensionValue myValue; /// the structure filled by constraint
+  SketcherPrs_DimensionStyle::DimensionValue myValue; /// the structure filled by constraint
 };
 
 #endif
\ No newline at end of file
index 753cff52ddb87012102ed44529acf4b593bfc0fe..d1d5e08a5d666124da16c0f8fe6ac08f58a0c03c 100644 (file)
 //
 
 #include "SketcherPrs_SensitivePoint.h"
+#include "SketcherPrs_SymbolPrs.h"
 
 #include <Graphic3d_ArrayOfPoints.hxx>
-#include "SketcherPrs_SymbolPrs.h"
+#include <Standard_Version.hxx>
 
 #define DEBUG_SENSITIVE_TO_BE_CORRECTED
 
@@ -36,18 +37,25 @@ SketcherPrs_SensitivePoint::SketcherPrs_SensitivePoint(
 Standard_Boolean SketcherPrs_SensitivePoint::Matches(SelectBasics_SelectingVolumeManager& theMgr,
                                                    SelectBasics_PickResult& thePickResult)
 {
-  Standard_Real aDepth      = RealLast();
-  Standard_Real aDistToCOG  = RealLast();
-  gp_Pnt aPnt = Point();
-  if (!theMgr.Overlaps (aPnt, aDepth))
-  {
-    thePickResult = SelectBasics_PickResult (aDepth, aDistToCOG);
-    return Standard_False;
-  }
+#if OCC_VERSION_HEX < 0x070400
+       Standard_Real aDepth = RealLast();
+       Standard_Real aDistToCOG = RealLast();
+       gp_Pnt aPnt = Point();
+       if (!theMgr.Overlaps(aPnt, aDepth))
+       {
+               thePickResult = SelectBasics_PickResult(aDepth, aDistToCOG);
+               return Standard_False;
+       }
 
-  aDistToCOG = aDepth;
-  thePickResult = SelectBasics_PickResult (aDepth, aDistToCOG);
-  return Standard_True;
+       aDistToCOG = aDepth;
+       thePickResult = SelectBasics_PickResult(aDepth, aDistToCOG);
+       return Standard_True;
+#else
+  gp_Pnt aPnt = Point();
+  if (theMgr.Overlaps (aPnt, thePickResult))
+         return Standard_True;
+  return Standard_False;
+#endif
 }
 
 gp_Pnt SketcherPrs_SensitivePoint::Point() const
index e4e0a8bcd1979b1a909902bb30cfc9dba67ea0a9..f7edd6d982cfd9475eb7786a6855f4dc29a63c36 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef SketcherPrs_SensitivePoint_H
 #define SketcherPrs_SensitivePoint_H
 
+#include <SelectBasics_EntityOwner.hxx>
 #include <Select3D_SensitiveEntity.hxx>
 #include <Standard_DefineHandle.hxx>
 
index e10434cf9e3f024f5ee28610480786d44dc8e377..f52a48c5dff67ffdeebcad64edd8d918ddd73637 100644 (file)
@@ -195,7 +195,7 @@ Handle(Image_AlienPixMap) SketcherPrs_SymbolPrs::icon()
   }
   // The icon for constraint is not found
   static const char aMsg[] = "Error! constraint images are not found";
-  cout<<aMsg<<endl;
+  std::cout<<aMsg<<std::endl;
   Events_InfoMessage("SketcherPrs_SymbolPrs", aMsg).send();
   myIconsMap[iconName()] = Handle(Image_AlienPixMap)();
   return Handle(Image_AlienPixMap)();
index 8b32e6944878444f03ae9722d5a7931c91f30645..2de9b6c82617b8f9785a69587131f0a18484e5fd 100644 (file)
 
 namespace SketcherPrs_Tools {
 
+static ParameterStyle MyStyle = ParameterValue;
+
+void setParameterStyle(ParameterStyle theStyle)
+{
+  MyStyle = theStyle;
+}
+
+ParameterStyle parameterStyle()
+{
+  return MyStyle;
+}
+
 AttributePtr getAttribute(ModelAPI_Feature* theFeature, const std::string& theAttrName)
 {
   AttributePtr anAttribute;
@@ -369,17 +381,6 @@ std::shared_ptr<GeomAPI_Pnt> getAnchorPoint(const ModelAPI_Feature* theConstrain
   return thePlane->to3D(aFlyoutPnt->x(), aFlyoutPnt->y());
 }
 
-void sendExpressionShownEvent(const bool& theState)
-{
-  static Events_ID anId = SketcherPrs_ParameterStyleMessage::eventId();
-  std::shared_ptr<SketcherPrs_ParameterStyleMessage> aMessage = std::shared_ptr
-    <SketcherPrs_ParameterStyleMessage>(new SketcherPrs_ParameterStyleMessage(anId, 0));
-  aMessage->setStyle(theState ? SketcherPrs_ParameterStyleMessage::ParameterText
-                              : SketcherPrs_ParameterStyleMessage::ParameterValue);
-  Events_Loop::loop()->send(aMessage);
-  Events_Loop::loop()->flush(anId);
-}
-
 void sendEmptyPresentationError(ModelAPI_Feature* theFeature, const std::string theError)
 {
   Events_InfoMessage("SketcherPrs_Tools",
index 5a719646c22e8c46447befffb461f15fbf6ab329..0b075f581b59bbba9440bd6ceaaed46080dce185 100644 (file)
 class GeomDataAPI_Point2D;
 class AIS_Dimension;
 
-//#define MyTextHeight 20
+namespace SketcherPrs_Tools {
 
-/// Message that style of visualization of parameter is changed.
-/// It will be shown as expression or value
-class SketcherPrs_ParameterStyleMessage : public Events_Message
-{
-public:
   /// \enum ParameterStyle lists styles of parameter
   enum ParameterStyle {
     ParameterValue, ///< using symbol with the parameter value
     ParameterText ///< using parameter text
   };
 
-public:
-  /// Creates an empty message
-  SKETCHERPRS_EXPORT SketcherPrs_ParameterStyleMessage(const Events_ID theID,
-                                                    const void* theSender = 0)
-  : Events_Message(theID, theSender) {}
-  /// The virtual destructor
-  SKETCHERPRS_EXPORT virtual ~SketcherPrs_ParameterStyleMessage() {}
-  /// Static. Returns EventID of the message.
-  SKETCHERPRS_EXPORT static Events_ID eventId()
-  {
-    static const char * MY_EVENT_PARAMETER_STYLE_ID("ParameterStyle");
-    return Events_Loop::eventByName(MY_EVENT_PARAMETER_STYLE_ID);
-  }
-  /// Returns a document stored in the message
-  SKETCHERPRS_EXPORT ParameterStyle style() const { return myStyle; }
-  /// Sets a document to the message
-  SKETCHERPRS_EXPORT void setStyle(ParameterStyle theStyle) { myStyle = theStyle; }
-private:
-  ParameterStyle myStyle; /// style of the parameter visualization
-};
+  /// Set dimensions parameters style
+  /// \param theStyle new style
+  SKETCHERPRS_EXPORT void setParameterStyle(ParameterStyle theStyle);
 
-namespace SketcherPrs_Tools {
+  /// Return dimensions parameters style
+  SKETCHERPRS_EXPORT ParameterStyle parameterStyle();
 
   /// Enumeration with modes for activation of selection custom presentations
   enum SelectionModes {
@@ -196,11 +175,6 @@ namespace SketcherPrs_Tools {
                                               const ModelAPI_Feature* theConstraint,
                                               const std::shared_ptr<GeomAPI_Ax3>& thePlane);
 
-  /// Sends event about expression visualization type is changed for dimension presentations
-  /// Sends event to redisplay all sub-features of composite feature
-  /// \param theState a new state
-  SKETCHERPRS_EXPORT void sendExpressionShownEvent(const bool& theState);
-
   /// Throws an exception(in debug mode) and sends a signal about necessity to hide the object
   /// \param theFeature a feature where AIS presentation is empty
   /// \param theError a debug error information
index a84c76a0868c9d8729ffc11d2acb07552c053c09..3f7d23326ef5544ae0d448665bd08a89e2c2c3bf 100644 (file)
@@ -105,7 +105,11 @@ const int MOUSE_SENSITIVITY_IN_PIXEL = 10;
 void displayedObjects(const Handle(AIS_InteractiveContext)& theAIS, AIS_ListOfInteractive& theList)
 {
   // Get from null point
-  theAIS->DisplayedObjects(theList, true);
+#if OCC_VERSION_HEX < 0x070400
+       theAIS->DisplayedObjects(theList, true);
+#else
+       theAIS->DisplayedObjects(theList);
+#endif
 }
 
 QString qIntListInfo(const QIntList& theValues, const QString& theSeparator = QString(", "))
@@ -200,8 +204,6 @@ bool XGUI_Displayer::display(ObjectPtr theObject, AISObjectPtr theAIS,
     //bool isCustomized = customizeObject(theObject);
 
     int aDispMode = isShading? Shading : Wireframe;
-    if (isShading)
-      anAISIO->Attributes()->SetFaceBoundaryDraw( Standard_True );
     anAISIO->SetDisplayMode(aDispMode);
     aContext->Display(anAISIO, aDispMode, 0, false, true, AIS_DS_Displayed);
     #ifdef TINSPECTOR
@@ -621,15 +623,16 @@ Handle(AIS_InteractiveContext) XGUI_Displayer::AISContext() const
     myContextId = aContext.get();
     if (!myWorkshop->selectionActivate()->isTrihedronActive())
       selectionActivate()->deactivateTrihedron(true);
-    aContext->DefaultDrawer()->VIsoAspect()->SetNumber(0);
-    aContext->DefaultDrawer()->UIsoAspect()->SetNumber(0);
+    // Do not modify default drawer. The same is done in ModuleBase_ResultPrs
+    //aContext->DefaultDrawer()->VIsoAspect()->SetNumber(0);
+    //aContext->DefaultDrawer()->UIsoAspect()->SetNumber(0);
 
     //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();
+    ModuleBase_IViewer::DefaultHighlightDrawer = aContext->HighlightStyle();
     //Handle(Prs3d_Drawer) aSelStyle = aContext->SelectionStyle();
     //double aDeflection =
     //  QString(ModelAPI_ResultConstruction::DEFAULT_DEFLECTION().c_str()).toDouble();
index f79eb6dbafb25c03de80c90fc8dbd9a68df0a804..e623a7e9098b3b67b19b11989838f67f036fa694 100644 (file)
@@ -42,7 +42,6 @@
 #include <GeomAPI_Shape.h>
 
 #include <ModelAPI_Events.h>
-#include <ModelAPI_ResultGroup.h>
 #include <ModelAPI_AttributeSelectionList.h>
 
 #include <QAction>
 #include <QGridLayout>
 #include <QListWidget>
 #include <QMainWindow>
+#include <QTimer>
 
 static const int LayoutMargin = 3;
 
+//********************************************************************
+bool getGroup(ModuleBase_ViewerPrsPtr thePrs, ResultGroupPtr& theResGroup,
+  FeaturePtr& theGroupFeature)
+{
+  ObjectPtr anObject = thePrs->object();
+  if (!anObject.get())
+    return false;
+
+  theResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anObject);
+  if (theResGroup.get()) {
+    theGroupFeature = ModelAPI_Feature::feature(theResGroup);
+  }
+  else {
+    theGroupFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(anObject);
+    if (theGroupFeature.get())
+      theResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(theGroupFeature->firstResult());
+  }
+  return theGroupFeature.get() && theResGroup.get();
+}
+
+//********************************************************************
+void updateHiddenShapes(Handle(ModuleBase_ResultPrs) thePrs, const TopoDS_ListOfShape& theShapes)
+{
+  TopoDS_ListOfShape aAlreadyHidden = thePrs->hiddenSubShapes();
+  TopoDS_ListOfShape::Iterator aShPIt(theShapes);
+  for (; aShPIt.More(); aShPIt.Next()) {
+    if (aAlreadyHidden.Contains(aShPIt.Value()))
+      aAlreadyHidden.Remove(aShPIt.Value());
+  }
+  thePrs->setSubShapeHidden(aAlreadyHidden);
+}
+
 //********************************************************************
 XGUI_FacesPanel::XGUI_FacesPanel(QWidget* theParent, XGUI_Workshop* theWorkshop)
   : QDockWidget(theParent), myIsActive(false), myWorkshop(theWorkshop)
 {
   setWindowTitle(tr("Hide Faces"));
+  setObjectName("Hide Faces");
+
   QAction* aViewAct = toggleViewAction();
   setStyleSheet("::title { position: relative; padding-left: 5px; text-align: left center }");
 
@@ -78,6 +112,10 @@ XGUI_FacesPanel::XGUI_FacesPanel(QWidget* theParent, XGUI_Workshop* theWorkshop)
 
   myListView->getControl()->setFocusPolicy(Qt::StrongFocus);
   myListView->getControl()->viewport()->installEventFilter(this);
+
+  XGUI_Displayer* aDisplayer = myWorkshop->displayer();
+  connect(aDisplayer, SIGNAL(objectDisplayed(ObjectPtr, AISObjectPtr)),
+    SLOT(onObjectDisplay(ObjectPtr, AISObjectPtr)));
 }
 
 //********************************************************************
@@ -88,7 +126,7 @@ void XGUI_FacesPanel::reset(const bool isToFlushRedisplay)
 
   std::map<ObjectPtr, TopoDS_ListOfShape> anObjectToShapes;
   std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) > anObjectToPrs;
-  QMap<int, std::shared_ptr<ModuleBase_ViewerPrs> >::const_iterator aIt;
+  QMap<int, ModuleBase_ViewerPrsPtr >::const_iterator aIt;
   for (aIt = myItems.cbegin(); aIt != myItems.cend(); aIt++) {
     getObjectsMapFromPrs(aIt.value(), anObjectToShapes, anObjectToPrs);
   }
@@ -100,7 +138,7 @@ void XGUI_FacesPanel::reset(const bool isToFlushRedisplay)
     aObjects.insert(aPrsIt->first);
     aPrsIt->second->setSubShapeHidden(anEmpty);
   }
-  std::set<std::shared_ptr<ModelAPI_Object> >::const_iterator aGrpIt;
+  std::set<ObjectPtr >::const_iterator aGrpIt;
   for (aGrpIt = myHiddenGroups.cbegin(); aGrpIt != myHiddenGroups.cend(); aGrpIt++)
     (*aGrpIt)->setDisplayed(true);
   myHiddenGroups.clear();
@@ -202,14 +240,9 @@ void XGUI_FacesPanel::restoreObjects(const std::set<ObjectPtr>& theHiddenObjects
   }
 
   // remove from myItes container
-  for (std::set<int>::const_iterator aToBeRemovedIt = anIndicesToBeRemoved.begin();
-    aToBeRemovedIt != anIndicesToBeRemoved.end(); aToBeRemovedIt++)
-  {
-    myItems.remove(*aToBeRemovedIt);
-  }
+  removeItems(anIndicesToBeRemoved);
   if (!anIndicesToBeRemoved.empty()) // means that myItems has been changed
     updateProcessedObjects(myItems, myItemObjects);
-  myListView->removeItems(anIndicesToBeRemoved);
 
   // remove from container of hidden objects
   for (std::set<ObjectPtr>::const_iterator aHiddenIt = theHiddenObjects.begin();
@@ -239,60 +272,75 @@ bool XGUI_FacesPanel::processAction(ModuleBase_ActionType theActionType)
 }
 
 //********************************************************************
-void XGUI_FacesPanel::getObjectsMapFromPrs(ModuleBase_ViewerPrsPtr thePrs,
+void XGUI_FacesPanel::getObjectsMapFromResult(ResultGroupPtr theResGroup,
+  FeaturePtr theGroupFeature,
   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;
-          }
+  // Process a grouip result
+  AttributeSelectionListPtr aSelectionList = theGroupFeature->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;
         }
       }
     }
   }
+}
+
+//********************************************************************
+void objectsMapFromPrs(ModuleBase_ViewerPrsPtr thePrs,
+  std::map<ObjectPtr, TopoDS_ListOfShape>& theObjectToShapes,
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) >& theObjectToPrs)
+{
+  ObjectPtr anObject = thePrs->object();
+  if (!anObject.get())
+    return;
+
+  // 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 {
-    // 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;
-    }
+    TopoDS_ListOfShape aListOfShapes;
+    aListOfShapes.Append(ModuleBase_Tools::getSelectedShape(thePrs));
+    theObjectToShapes[anObject] = aListOfShapes;
+    theObjectToPrs[anObject] = aResultPrs;
   }
 }
 
+//********************************************************************
+void XGUI_FacesPanel::getObjectsMapFromPrs(ModuleBase_ViewerPrsPtr thePrs,
+  std::map<ObjectPtr, TopoDS_ListOfShape>& theObjectToShapes,
+  std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) >& theObjectToPrs)
+{
+  ResultGroupPtr aResGroup;
+  FeaturePtr aGroupFeature;
+  if (getGroup(thePrs, aResGroup, aGroupFeature))
+    getObjectsMapFromResult(aResGroup, aGroupFeature, theObjectToShapes, theObjectToPrs);
+  else
+    objectsMapFromPrs(thePrs, theObjectToShapes, theObjectToPrs);
+}
+
 //********************************************************************
 void XGUI_FacesPanel::processSelection()
 {
@@ -315,16 +363,14 @@ void XGUI_FacesPanel::processSelection()
     if (!anObject.get())
       continue;
 
-    ResultGroupPtr aResGroup = std::dynamic_pointer_cast<ModelAPI_ResultGroup>(anObject);
-    if (aResGroup.get()) {
-      FeaturePtr aFeature = ModelAPI_Feature::feature(aResGroup);
-      if (aFeature.get()) {
-        AttributeSelectionListPtr aSelectionListAttr =
-          aFeature->data()->selectionList("group_list");
-        std::string aType = aSelectionListAttr->selectionType();
-        if (aType != "Faces")
-          continue;
-      }
+    ResultGroupPtr aResGroup;
+    FeaturePtr aGroupFeature;
+    if (getGroup(aPrs, aResGroup, aGroupFeature)) {
+      AttributeSelectionListPtr aSelectionListAttr =
+        aGroupFeature->data()->selectionList("group_list");
+      std::string aType = aSelectionListAttr->selectionType();
+      if (aType != "Faces")
+        continue;
     }
     else {
       GeomShapePtr aShapePtr = aPrs->shape();
@@ -337,11 +383,15 @@ void XGUI_FacesPanel::processSelection()
     if (myListView->hasItem(aItemName))
       continue;
 
-    getObjectsMapFromPrs(aPrs, anObjectToShapes, anObjectToPrs);
-    if (aResGroup.get() && aResGroup->isDisplayed()) {
-      aResGroup->setDisplayed(false);
-      myHiddenGroups.insert(aResGroup);
+    if (aResGroup.get()) {
+      if (aResGroup->isDisplayed()) {
+        aResGroup->setDisplayed(false);
+        myHiddenGroups.insert(aResGroup);
+      }
+      getObjectsMapFromResult(aResGroup, aGroupFeature, anObjectToShapes, anObjectToPrs);
     }
+    else
+      objectsMapFromPrs(aPrs, anObjectToShapes, anObjectToPrs);
 
     // The code is dedicated to remove already selected items if they are selected twice
     // It can happen in case of groups selection
@@ -401,12 +451,7 @@ void XGUI_FacesPanel::processSelection()
   }
 
   // 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);
-  }
+  removeItems(aToRemove);
   if (isModified) {
     updateProcessedObjects(myItems, myItemObjects);
     flushRedisplay();
@@ -441,9 +486,10 @@ bool XGUI_FacesPanel::processDelete()
   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);
+    ResultGroupPtr aResGroup;
+    FeaturePtr aGroupFeature;
+    if (getGroup((*aIt), aResGroup, aGroupFeature)) {
+      std::set<ObjectPtr >::iterator aGrpIt = myHiddenGroups.find(aResGroup);
       if (aGrpIt != myHiddenGroups.end()) {
         aResGroup->setDisplayed(true);
         myHiddenGroups.erase(aGrpIt);
@@ -474,7 +520,7 @@ bool XGUI_FacesPanel::processDelete()
 
 //********************************************************************
 bool XGUI_FacesPanel::redisplayObjects(
-  const std::set<std::shared_ptr<ModelAPI_Object> >& theObjects)
+  const std::set<ObjectPtr >& theObjects)
 {
   if (theObjects.empty())
     return false;
@@ -501,24 +547,21 @@ void XGUI_FacesPanel::updateProcessedObjects(QMap<int, ModuleBase_ViewerPrsPtr>
   for (QMap<int, ModuleBase_ViewerPrsPtr>::const_iterator anIt = theItems.begin();
        anIt != theItems.end(); anIt++) {
     ModuleBase_ViewerPrsPtr aPrs = anIt.value();
-    ObjectPtr anObject = aPrs.get() ? aPrs->object() : ObjectPtr();
-    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);
+    ResultGroupPtr aResGroup;
+    FeaturePtr aGroupFeature;
+    if (getGroup(aPrs, aResGroup, aGroupFeature)) {
+      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(aPrs->object()) == theObjects.end())
+        theObjects.insert(aPrs->object());
+    }
   }
 }
 
@@ -540,7 +583,7 @@ void XGUI_FacesPanel::onTransparencyChanged()
 {
   std::map<ObjectPtr, TopoDS_ListOfShape> anObjectToShapes;
   std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) > anObjectToPrs;
-  QMap<int, std::shared_ptr<ModuleBase_ViewerPrs> >::const_iterator aIt;
+  QMap<int, ModuleBase_ViewerPrsPtr >::const_iterator aIt;
   for (aIt = myItems.cbegin(); aIt != myItems.cend(); aIt++) {
     getObjectsMapFromPrs(aIt.value(), anObjectToShapes, anObjectToPrs);
   }
@@ -573,3 +616,97 @@ void XGUI_FacesPanel::flushRedisplay() const
     anObjectBrowser->updateAllIndexes();
   myWorkshop->viewer()->update();
 }
+
+
+//********************************************************************
+void XGUI_FacesPanel::onObjectDisplay(ObjectPtr theObject, AISObjectPtr theAIS)
+{
+  bool aContains = false;
+  QMap<int, ModuleBase_ViewerPrsPtr>::iterator aIt;
+  for (aIt = myItems.begin(); aIt != myItems.end(); aIt++) {
+    ModuleBase_ViewerPrsPtr aPrs = aIt.value();
+    if (aPrs->object() == theObject) {
+      aContains = true;
+      break;
+    }
+  }
+  if (aContains) {
+    ResultGroupPtr aResGroup;
+    FeaturePtr aGroupFeature;
+    std::map<ObjectPtr, TopoDS_ListOfShape> aObjectToShapes;
+    std::map<ObjectPtr, Handle(ModuleBase_ResultPrs)> aObjectToPrs;
+    std::set<ObjectPtr> aObjects;
+    std::set<int> aIdsToRem;
+
+    TopoDS_ListOfShape aHideShapes;
+    std::map<ObjectPtr, TopoDS_ListOfShape>::const_iterator aSIt;
+    for (aIt = myItems.begin(); aIt != myItems.end(); aIt++) {
+      ModuleBase_ViewerPrsPtr aPrs = aIt.value();
+      if (getGroup(aPrs, aResGroup, aGroupFeature)) {
+        getObjectsMapFromResult(aResGroup, aGroupFeature, aObjectToShapes, aObjectToPrs);
+        if (aResGroup == theObject) {
+          // If group is displayed it means that it has to be deleted from the Faces list and all
+          // corresponded faces have been restored
+          for (aSIt = aObjectToShapes.begin(); aSIt != aObjectToShapes.end(); aSIt++) {
+            TopoDS_ListOfShape aShapes = aSIt->second;
+            Handle(ModuleBase_ResultPrs) aPrs = aObjectToPrs[aSIt->first];
+            TopoDS_ListOfShape aAlreadyHidden = aPrs->hiddenSubShapes();
+            TopoDS_ListOfShape::Iterator aShPIt(aShapes);
+            for (; aShPIt.More(); aShPIt.Next()) {
+              if (aAlreadyHidden.Contains(aShPIt.Value()))
+                aAlreadyHidden.Remove(aShPIt.Value());
+            }
+            aPrs->setSubShapeHidden(aAlreadyHidden);
+            aObjects.insert(aSIt->first);
+          }
+          aIdsToRem.insert(aIt.key());
+        }
+        else {
+          std::map<ObjectPtr, Handle(ModuleBase_ResultPrs)>::iterator aPIt =
+            aObjectToPrs.find(theObject);
+          if (aPIt != aObjectToPrs.end()) {
+            ObjectPtr aObj = aPIt->first;
+            if (aObj == theObject) {
+              Handle(ModuleBase_ResultPrs) aPrs = aPIt->second;
+              TopoDS_ListOfShape aShapes = aObjectToShapes[aObj];
+              aHideShapes.Append(aShapes);
+              aObjects.insert(aObj);
+            }
+          }
+        }
+      }
+      else {
+        if (aPrs->object() == theObject) {
+          TopoDS_Shape aShape = aPrs->shape()->impl<TopoDS_Shape>();
+          if (!aShape.IsNull())
+            aHideShapes.Append(aShape);
+          aPrs->setInteractive(theAIS->impl<Handle(AIS_InteractiveObject)>());
+        }
+      }
+    }
+    double aTransp = transparency();
+    if (aHideShapes.Size() > 0) {
+      Handle(ModuleBase_ResultPrs) aResultPrs = Handle(ModuleBase_ResultPrs)::DownCast(
+        theAIS->impl<Handle(AIS_InteractiveObject)>());
+      if (!aResultPrs.IsNull()) {
+        aResultPrs->setSubShapeHidden(aHideShapes);
+        aResultPrs->setHiddenSubShapeTransparency(aTransp);
+        aObjects.insert(theObject);
+      }
+    }
+    removeItems(aIdsToRem);
+    myWorkshop->selector()->clearSelection();
+    if (redisplayObjects(aObjects))
+      QTimer::singleShot(50, this, SLOT(flushRedisplay()));
+  }
+}
+
+void XGUI_FacesPanel::removeItems(std::set<int> theIds)
+{
+  if (theIds.empty())
+    return;
+  myListView->removeItems(theIds);
+  std::set<int>::const_iterator aRIt;
+  for (aRIt = theIds.begin(); aRIt != theIds.end(); aRIt++)
+    myItems.remove(*aRIt);
+}
index d442ee6b87f174b51cfc1a7e0f138f9f882fbfd8..fe9f02b178173dd063f793be45d7d056b7cf0d46 100644 (file)
 
 #include "XGUI.h"
 
-#include <ModelAPI_Object.h>
-
 #include <ModuleBase_ActionType.h>
 #include <ModuleBase_Definitions.h>
 #include <ModuleBase_ViewerPrs.h>
 #include <ModuleBase_ResultPrs.h>
 
+#include <ModelAPI_Object.h>
+#include <ModelAPI_ResultGroup.h>
+#include <ModelAPI_Feature.h>
+
+#include <GeomAPI_AISObject.h>
+
 #include <SelectMgr_ListOfFilter.hxx>
 #include <TopoDS_Shape.hxx>
 
@@ -100,12 +104,12 @@ public:
   /// Returns true if the object is in internal container of hidden objects by this panel
   /// \param theObject a checked object
   /// \return boolean value
-  bool isObjectHiddenByPanel(const std::shared_ptr<ModelAPI_Object>& theObject) const
+  bool isObjectHiddenByPanel(const ObjectPtr& theObject) const
   { return myHiddenObjects.find(theObject) != myHiddenObjects.end(); }
 
   /// Removed faces of the objects from the panel
   /// \param container of objects
-  void restoreObjects(const std::set<std::shared_ptr<ModelAPI_Object> >& theHiddenObjects);
+  void restoreObjects(const std::set<ObjectPtr >& theHiddenObjects);
 
   /// Returns true if the event is processed. The default implementation is empty, returns false.
   virtual bool processAction(ModuleBase_ActionType theActionType);
@@ -124,6 +128,9 @@ public:
 
   XGUI_Workshop* workshop() const { return myWorkshop; }
 
+public slots:
+  /// Slot called on an object erase
+  void onObjectDisplay(ObjectPtr theObject, AISObjectPtr theAIS);
 
 protected:
   /// Reimplementation to emit a signal about the panel close
@@ -141,13 +148,13 @@ private:
   /// Redisplay objects.
   /// \param theObjects container of objects
   /// \return true if some of objects was redisplayed
-  static bool redisplayObjects(const std::set<std::shared_ptr<ModelAPI_Object> >& theObjects);
+  static bool redisplayObjects(const std::set<ObjectPtr>& theObjects);
 
   /// 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);
+  static void updateProcessedObjects(QMap<int, ModuleBase_ViewerPrsPtr> theItems,
+                                     std::set<ObjectPtr>& 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
@@ -155,16 +162,22 @@ private:
   /// \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,
+  void getObjectsMapFromResult(ResultGroupPtr theResGroup, FeaturePtr theGroupFeature,
     std::map<ObjectPtr, TopoDS_ListOfShape>& theObjectsToShapes,
     std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) >& theObjectToPrs);
 
+  void getObjectsMapFromPrs(ModuleBase_ViewerPrsPtr thePrs,
+    std::map<ObjectPtr, TopoDS_ListOfShape>& theObjectToShapes,
+    std::map<ObjectPtr, Handle(ModuleBase_ResultPrs) >& theObjectToPrs);
+
   /// Returns true if transparency choice is checked
   /// \return boolean value
   bool useTransparency() const;
 
   double transparency() const;
 
+  void removeItems(std::set<int> theIds);
+
 protected slots:
   /// Deletes element in list of items
   void onDeleteItem();
@@ -175,12 +188,12 @@ protected slots:
   /// Closes faces panel restore all hidden faces by calling reset()
   void onClosed();
 
-private:
   /// Flushes redisplay event and perform update of object browser icons
   /// (objects might be hidden/shown)
   void flushRedisplay() const;
 
-protected:
+private:
+
   QCheckBox* myHiddenOrTransparent; ///< if checked - transparent, else hidden
   ModuleBase_ListView* myListView; ///< list control of processed faces
   XGUI_Workshop* myWorkshop; ///< workshop
@@ -189,9 +202,9 @@ protected:
   int myLastItemIndex; ///< last index to be used in the map of items for the next added item
 
   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
+  std::set<ObjectPtr > myItemObjects; ///< cached objects of myItems
+  std::set<ObjectPtr > myHiddenObjects; ///< hidden objects
+  std::set<ObjectPtr > myHiddenGroups; ///< hidden objects
 };
 
 #endif
\ No newline at end of file
index 95ae953a1bbe81cdc0dee04e8904c31ae4a4382d..d82af690252fb778b08f9540b02d167e7897cb51 100644 (file)
@@ -229,6 +229,7 @@ void XGUI_InspectionPanel::setSubShapeValue(SudShape theId, int theVal)
 //********************************************************************
 void XGUI_InspectionPanel::clearContent()
 {
+  myStackWgt->setCurrentIndex(myFeaturePanelId);
   setName("");
   for (int i = 0; i <= VertexId; i++) {
     mySubShapesTab->item((SudShape)i, 1)->setText("");
@@ -263,7 +264,6 @@ void XGUI_InspectionPanel::onSelectionChanged()
       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());
@@ -274,8 +274,11 @@ void XGUI_InspectionPanel::onSelectionChanged()
           }
         }
       }
-      if (aShape.IsNull())
+      if (aShape.IsNull()) {
+        myStackWgt->setCurrentIndex(myFeaturePanelId);
         return;
+      }
+      myStackWgt->setCurrentIndex(myShapePanelId);
       GeomShapePtr aShapePtr(new GeomAPI_Shape());
       aShapePtr->setImpl(new TopoDS_Shape(aShape));
 
index 68c1b535de7750db8d196df5116e098b665930aa..347a7e62fc7935b0067c9a752b2d7aa3755c8560 100644 (file)
 #include "ModuleBase_OperationFeature.h"
 #include "ModuleBase_Tools.h"
 
-#include "ModelAPI_CompositeFeature.h"
-#include "ModelAPI_Session.h"
+#include <Config_Translator.h>
+
+#include <ModelAPI_CompositeFeature.h>
+#include <ModelAPI_Session.h>
 
 #include <XGUI_PropertyPanel.h>
 #include <QToolButton>
@@ -336,7 +338,13 @@ bool XGUI_OperationMgr::canStopOperation(ModuleBase_Operation* theOperation,
   if (isGrantedOperation(theOperation->id()))
     return true;
   if (theOperation && theOperation->isModified()) {
-    QString aTitle = theOperation->getDescription()->description();
+    ModuleBase_OperationFeature* aOp = dynamic_cast<ModuleBase_OperationFeature*>(theOperation);
+    std::string aContext;
+    if (aOp)
+      aContext = aOp->feature()->getKind();
+    QString aTitle = Config_Translator::translate(aContext,
+      theOperation->getDescription()->description().toStdString()).c_str();
+
     if (theMessageKind == XGUI_AbortOperationMessage) {
       QString aMessage = tr("%1 operation will be aborted.").arg(aTitle);
       myActiveMessageBox = createMessageBox(aMessage);
index d0217ac1a0ec73e3f4cc3a2b6f5b3416ea077b79..0099869f50e86fd6cc92f88e407822eabf49d33e 100644 (file)
@@ -220,16 +220,24 @@ void XGUI_PropertyPanel::createContentPanel(FeaturePtr theFeature)
   if (theFeature->isAction() || !theFeature->data())
     return;
 
+  ModuleBase_Operation* anOperation = myOperationMgr->currentOperation();
   if (myWidgets.empty()) {
-    ModuleBase_Operation* anOperation = myOperationMgr->currentOperation();
     QString aXmlRepr = anOperation->getDescription()->xmlRepresentation();
 
     ModuleBase_WidgetFactory aFactory(aXmlRepr.toStdString(), myOperationMgr->workshop());
-    aFactory.createPanel(contentWidget(), theFeature);
-    /// Apply button should be update if the feature was modified by the panel
+    ModuleBase_PageBase* aPage = contentWidget();
+    aFactory.createPanel(aPage, theFeature);
+    // update model widgets if exist
+    setModelWidgets(aPage->modelWidgets());
+    // Apply button should be update if the feature was modified by the panel
     myOperationMgr->onValidateOperation();
   }
-  updateApplyPlusButton(theFeature);
+  ModuleBase_OperationFeature* aFeatureOp =
+    dynamic_cast<ModuleBase_OperationFeature*>(anOperation);
+  if (aFeatureOp && (!aFeatureOp->isEditOperation()))
+    updateApplyPlusButton(theFeature);
+  else
+    findButton(PROP_PANEL_OK_PLUS)->setVisible(false);
 }
 
 void XGUI_PropertyPanel::updateApplyPlusButton(FeaturePtr theFeature)
@@ -473,7 +481,7 @@ bool XGUI_PropertyPanel::focusNextPrevChild(bool theIsNext)
     ModuleBase_ModelWidget* aNewFocusMWidget = ModuleBase_ModelWidget::findModelWidget(this,
                                                                               aNewFocusWidget);
     if (aNewFocusMWidget) {
-      if (aFocusMWidget) {
+      if (aFocusMWidget && (aFocusMWidget != aNewFocusMWidget)) {
         aFocusMWidget->setHighlighted(false);
       }
       aNewFocusMWidget->emitFocusInWidget();
index b7c1a65bca80bb5cf842574e5010e8fdd1ba2125..49bcdcbd375174e0c6ad221b217250cf38c26ad8 100644 (file)
@@ -142,9 +142,14 @@ void XGUI_SelectionMgr::onViewerSelection()
   myLastSelectionPlace = ModuleBase_ISelection::Viewer;
   QList<ModuleBase_ViewerPrsPtr> aValues;
   Handle(AIS_InteractiveContext) aContext = myWorkshop->viewer()->AISContext();
-  if (!aContext.IsNull())
+  if (!aContext.IsNull()) {
     aValues = selection()->getSelected(ModuleBase_ISelection::Viewer);
-
+    // Update is necessary for OCCT 7.4.0: when it is clears selection it doesn't updates viewer
+#if OCC_VERSION_HEX == 0x070400
+    if (aValues.isEmpty())
+      aContext->UpdateCurrentViewer();
+#endif
+  }
   QObjectPtrList anObjects;
   convertToObjectBrowserSelection(aValues, anObjects);
   myWorkshop->objectBrowser()->setObjectsSelected(anObjects);
index fbff53c41f2180237f277379746c294925d51237..82154a769367c2167bca85c0aa9ede133f73834a 100644 (file)
@@ -122,7 +122,7 @@ void XGUI_ViewerProxy::setViewProjection(double theX, double theY, double theZ,
     aView3d->SetProj(theX, theY, theZ);
     aView3d->SetTwist( theTwist );
     aView3d->FitAll(0.01, false);
-    aView3d->SetZSize(0.);
+    //aView3d->SetZSize(0.);
     if (aView3d->Depth() < 0.1)
       aView3d->DepthFitAll();
   }
index 5ed8b452746280f8ad6c2b3f5301cc0e4d51ba3a..bb845edb3f7407a49bba83d7de00642fc69c5d7d 100644 (file)
@@ -226,7 +226,15 @@ XGUI_Workshop::XGUI_Workshop(XGUI_SalomeConnector* theConnector)
   // Load translations
   QStringList aLangs;
   aLangs << "*_en.ts"; // load by default eng translations
+
+  /// If version of OCCT is 7.4.0 or more then it means that
+  /// this is version of SALOME older then 9.4.0
+#if OCC_VERSION_HEX >= 0x070400
+  QString aCurrLang = aResMgr->language();
+#else
   QString aCurrLang = aResMgr->stringValue("language", "language", "en");
+#endif
+
   if(aCurrLang != "en") {
     aLangs << "*_" + aCurrLang + ".ts"; // then replace with translated files
   }
@@ -866,7 +874,7 @@ void XGUI_Workshop::onOperationStopped(ModuleBase_Operation* theOperation)
 
   if (Config_PropManager::boolean("Windows", "use_hide_faces_panel")) {
     if (!theOperation->isHideFacesVisible())
-      myFacesPanel->hide();
+      myFacesPanel->close();
   }
 }
 
@@ -1285,6 +1293,7 @@ void XGUI_Workshop::onImportPart()
   if (abortAllOperations()) {
     ModuleBase_OperationFeature* anImportPartOp = dynamic_cast<ModuleBase_OperationFeature*>(
         module()->createOperation(ExchangePlugin_ImportPart::ID()));
+    myPropertyPanel->updateApplyPlusButton(anImportPartOp->feature());
     operationMgr()->startOperation(anImportPartOp);
   }
 }
@@ -1295,6 +1304,7 @@ void XGUI_Workshop::onExportPart()
   if (abortAllOperations()) {
     ModuleBase_OperationFeature* anExportPartOp = dynamic_cast<ModuleBase_OperationFeature*>(
         module()->createOperation(ExchangePlugin_ExportPart::ID()));
+    myPropertyPanel->updateApplyPlusButton(anExportPartOp->feature());
     operationMgr()->startOperation(anExportPartOp);
   }
 }
@@ -1472,6 +1482,7 @@ QDockWidget* XGUI_Workshop::createObjectBrowser(QWidget* theParent)
   myObjectBrowser->initialize(myModule->rootNode());
   myModule->customizeObjectBrowser(myObjectBrowser);
   aObjDock->setWidget(myObjectBrowser);
+  aObjDock->setObjectName("Object browser");
 
   connect(myObjectBrowser, SIGNAL(sizeChanged()), SLOT(onDockSizeChanged()));
 
@@ -1859,6 +1870,9 @@ void XGUI_Workshop::deleteObjects()
   }
 
   QObjectPtrList anObjects = mySelector->selection()->selectedObjects();
+  if (anObjects.isEmpty())
+    return;
+
   if (!abortAllOperations())
     return;
 
index ebe3b8867122a019377dd0f3feda279c4cd28c1f..859c4018b776aab83ead3ff470427fdd4979088d 100644 (file)
         <source>Validate operation</source>
         <translation>Valider l&apos;opération</translation>
     </message>
+    <message>
+        <source>Part files (*.shaperpart);;All files (*.*)</source>
+        <translation>Fichiers pièces (*.shaperpart);;Tous les fichiers (*.*)</translation>
+    </message>
 </context>
 <context>
     <name>XGUI_ActionsMgr</name>
         <source>Windows</source>
         <translation>Fenêtres</translation>
     </message>
+    <message>
+        <source>Iso-lines...</source>
+        <translation></translation>
+    </message>
 </context>
 <context>
     <name>XGUI_DataTree</name>
     </message>
     <message>
         <source>INF_DESK_TOOLBAR_STANDARD</source>
-        <translation type="unfinished"></translation>
+        <translation>INF_DESK_TOOLBAR_STANDARD</translation>
     </message>
     <message>
         <source>Redo</source>
@@ -661,5 +669,33 @@ Supprimer des objets du panneau à afficher ?</translation>
         <source>Results not found</source>
         <translation>Résultats non trouvés</translation>
     </message>
+    <message>
+        <source>Export part...</source>
+        <translation>Partie export...</translation>
+    </message>
+    <message>
+        <source>Export a part of the current document into a file</source>
+        <translation>Exporter une partie du document courant dans un fichier</translation>
+    </message>
+    <message>
+        <source>Import part...</source>
+        <translation>Importer une partie...</translation>
+    </message>
+    <message>
+        <source>Import structure of a part</source>
+        <translation>Structure d&apos;importation d&apos;une pièce</translation>
+    </message>
+    <message>
+        <source>Import shape...</source>
+        <translation>Forme de l&apos;importation...</translation>
+    </message>
+    <message>
+        <source>Import shape from a file</source>
+        <translation>Importer une forme à partir d&apos;un fichier</translation>
+    </message>
+    <message>
+        <source>Number of Iso-lines</source>
+        <translation>Nombre de lignes Iso-Lines</translation>
+    </message>
 </context>
 </TS>
index 9b958d9e758cde5347d49338cc273f11ccf7af60..2178d9dda21d4ebf86037792b770e8fcd2f5c9c2 100644 (file)
@@ -92,5 +92,8 @@
      <file>pictures/normal-view.png</file>
      <file>pictures/move_to_end.png</file>
      <file>pictures/move_to_end_split.png</file>
+     <file>pictures/ArrowCursor.png</file>
+     <file>pictures/CrossCursor.png</file>
+     <file>pictures/HandCursor.png</file>
  </qresource>
  </RCC>
diff --git a/src/XGUI/pictures/ArrowCursor.png b/src/XGUI/pictures/ArrowCursor.png
new file mode 100644 (file)
index 0000000..de53b10
Binary files /dev/null and b/src/XGUI/pictures/ArrowCursor.png differ
diff --git a/src/XGUI/pictures/CrossCursor.png b/src/XGUI/pictures/CrossCursor.png
new file mode 100644 (file)
index 0000000..230d586
Binary files /dev/null and b/src/XGUI/pictures/CrossCursor.png differ
diff --git a/src/XGUI/pictures/HandCursor.png b/src/XGUI/pictures/HandCursor.png
new file mode 100644 (file)
index 0000000..0d66435
Binary files /dev/null and b/src/XGUI/pictures/HandCursor.png differ
index a8e5ca417ead1a93deff2523f8c6711d831e2f74..dd856efbc55bdde1d890a1a149084c4a55e2d5a6 100644 (file)
@@ -21,51 +21,55 @@ ENABLE_TESTING()
 
 SET(RESTRICTED_ROOT_DIR $ENV{RESTRICTED_ROOT_DIR} CACHE PATH "Path to the restricted repository")
 
+set(hdfFilesRestr "")
 if (EXISTS ${RESTRICTED_ROOT_DIR})
   file(GLOB hdfFilesRestr "${RESTRICTED_ROOT_DIR}/SHAPER/test.hdfs/*.hdf")
+endif()
+
+file(GLOB hdfFilesCur "${CMAKE_CURRENT_SOURCE_DIR}/*.hdf")
+set(hdfFilesRestr ${hdfFilesCur} ${hdfFilesRestr})
 
-  if (WIN32) # different separators and path to libraries variable name
-    SET(_JUSTPATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_BIN};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES};${SUIT_LIB_DIR};$ENV{PATH}")
-    STRING(REPLACE "\\" "/" _JUSTPATH "${_JUSTPATH}")
-    STRING(REPLACE ";" "\\;" _JUSTPATH "${_JUSTPATH}")
-    SET(_PYTHONPATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_ADDONS};$ENV{PYTHONPATH}")
-    STRING(REPLACE "\\" "/" _PYTHONPATH "${_PYTHONPATH}")
-    STRING(REPLACE ";" "\\;" _PYTHONPATH "${_PYTHONPATH}")
-  else()
-    SET(_LD_LIBRARY_PATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_BIN}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES}:${SUIT_LIB_DIR}:$ENV{LD_LIBRARY_PATH}")
-    SET(_PYTHONPATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_ADDONS}:$ENV{PYTHONPATH}")
-  endif()
+if (WIN32) # different separators and path to libraries variable name
+  SET(_JUSTPATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_BIN};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES};${SUIT_LIB_DIR};$ENV{PATH}")
+  STRING(REPLACE "\\" "/" _JUSTPATH "${_JUSTPATH}")
+  STRING(REPLACE ";" "\\;" _JUSTPATH "${_JUSTPATH}")
+  SET(_PYTHONPATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES};${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_ADDONS};$ENV{PYTHONPATH}")
+  STRING(REPLACE "\\" "/" _PYTHONPATH "${_PYTHONPATH}")
+  STRING(REPLACE ";" "\\;" _PYTHONPATH "${_PYTHONPATH}")
+else()
+  SET(_LD_LIBRARY_PATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_BIN}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES}:${SUIT_LIB_DIR}:$ENV{LD_LIBRARY_PATH}")
+  SET(_PYTHONPATH "${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_SWIG}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_PLUGIN_FILES}:${CMAKE_INSTALL_PREFIX}/${SHAPER_INSTALL_ADDONS}:$ENV{PYTHONPATH}")
+endif()
 
-  foreach(eachFilePath ${hdfFilesRestr})
-    # Strip the ".hdf" suffix
-    GET_FILENAME_COMPONENT(aTestName ${eachFilePath} NAME_WE)
-    # Check corresponding ".py" file with reference data exists
-    IF(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${aTestName}.py")
-      MESSAGE(WARNING "File ${aTestName}.py containing reference data does not exist")
-      continue()
-    ENDIF()
+foreach(eachFilePath ${hdfFilesRestr})
+  # Strip the ".hdf" suffix
+  GET_FILENAME_COMPONENT(aTestName ${eachFilePath} NAME_WE)
+  # Check corresponding ".py" file with reference data exists
+  IF(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${aTestName}.py")
+    MESSAGE(WARNING "File ${aTestName}.py containing reference data does not exist")
+    continue()
+  ENDIF()
 
-    # Add "SubprojectName_" prefix
-    GET_FILENAME_COMPONENT(aSubprojectName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
-    SET(aTestName "${aSubprojectName}_${aTestName}")
+  # Add "SubprojectName_" prefix
+  GET_FILENAME_COMPONENT(aSubprojectName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
+  SET(aTestName "${aSubprojectName}_${aTestName}")
 
-    # Full path to the python test file being executed
-    SET(aTestFilePath "${eachFilePath}")
-    IF(EXISTS ${aTestFilePath})
-      ADD_TEST(NAME ${aTestName} COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/test.py" "$ENV{KERNEL_ROOT_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" "${aTestFilePath}")
-      if (WIN32) # different path to libraries variable name
-        SET_TESTS_PROPERTIES(${aTestName} PROPERTIES ENVIRONMENT "PATH=${_JUSTPATH};PYTHONPATH=${_PYTHONPATH}"
-          LABELS "models_hdf")
-      else()
-        SET_TESTS_PROPERTIES(${aTestName} PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${_LD_LIBRARY_PATH};PYTHONPATH=${_PYTHONPATH}"
-          LABELS "models_hdf")
-      endif()
-      # Debug output...
-      # MESSAGE(STATUS "Test added: ${aTestName} file: ${aTestFilePath}")
-    ELSE(EXISTS ${aTestFilePath})
-      MESSAGE(WARNING "Can not find the test file: ${aTestFilePath}")
-    ENDIF(EXISTS ${aTestFilePath})
-  endforeach(eachFilePath ${ARGN})
+  # Full path to the python test file being executed
+  SET(aTestFilePath "${eachFilePath}")
+  IF(EXISTS ${aTestFilePath})
+    ADD_TEST(NAME ${aTestName} COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/test.py" "$ENV{KERNEL_ROOT_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}" "${aTestFilePath}")
+    if (WIN32) # different path to libraries variable name
+      SET_TESTS_PROPERTIES(${aTestName} PROPERTIES ENVIRONMENT "PATH=${_JUSTPATH};PYTHONPATH=${_PYTHONPATH}"
+        LABELS "models_hdf")
+    else()
+      SET_TESTS_PROPERTIES(${aTestName} PROPERTIES ENVIRONMENT "LD_LIBRARY_PATH=${_LD_LIBRARY_PATH};PYTHONPATH=${_PYTHONPATH}"
+        LABELS "models_hdf")
+    endif()
+    # Debug output...
+    # MESSAGE(STATUS "Test added: ${aTestName} file: ${aTestFilePath}")
+  ELSE(EXISTS ${aTestFilePath})
+    MESSAGE(WARNING "Can not find the test file: ${aTestFilePath}")
+  ENDIF(EXISTS ${aTestFilePath})
+endforeach(eachFilePath ${ARGN})
 
-  ADD_CUSTOM_TARGET(run_hdf_tests COMMAND ${CMAKE_CTEST_COMMAND} -C "${CMAKE_BUILD_TYPE}" -L "models_hdf")
-endif()
+ADD_CUSTOM_TARGET(run_hdf_tests COMMAND ${CMAKE_CTEST_COMMAND} -C "${CMAKE_BUILD_TYPE}" -L "models_hdf")
diff --git a/test.hdfs/CuveN4_Tubulures.py b/test.hdfs/CuveN4_Tubulures.py
new file mode 100644 (file)
index 0000000..b7dbc59
--- /dev/null
@@ -0,0 +1,28 @@
+# 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
+#
+
+if __name__ == "__main__":
+  aPartFeature = locals()["Part_1"]
+  model.testNbResults(aPartFeature, 1)
+  model.testNbSubResults(aPartFeature, [0])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.SOLID, [552])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.FACE, [4086])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.EDGE, [17086])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.VERTEX, [34172])
+  model.testResultsVolumes(aPartFeature, [55.645173358299])
diff --git a/test.hdfs/Test3061.hdf b/test.hdfs/Test3061.hdf
new file mode 100644 (file)
index 0000000..5593af7
Binary files /dev/null and b/test.hdfs/Test3061.hdf differ
diff --git a/test.hdfs/Test3061.py b/test.hdfs/Test3061.py
new file mode 100644 (file)
index 0000000..f477663
--- /dev/null
@@ -0,0 +1,28 @@
+# 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
+#
+
+if __name__ == "__main__":
+  aPartFeature = locals()["Part_1"]
+  model.testNbResults(aPartFeature, 1)
+  model.testNbSubResults(aPartFeature, [0])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.SOLID, [3])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.FACE, [33])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.EDGE, [162])
+  model.testNbSubShapes(aPartFeature, GeomAPI_Shape.VERTEX, [324])
+  model.testResultsVolumes(aPartFeature, [75056.139056284388])