]> SALOME platform Git repositories - modules/shaper.git/commitdiff
Salome HOME
Issue #1366: Added Union feature
authordbv <dbv@opencascade.com>
Mon, 20 Jun 2016 12:08:14 +0000 (15:08 +0300)
committerdbv <dbv@opencascade.com>
Tue, 21 Jun 2016 12:17:13 +0000 (15:17 +0300)
12 files changed:
src/FeaturesPlugin/CMakeLists.txt
src/FeaturesPlugin/FeaturesPlugin_Boolean.cpp
src/FeaturesPlugin/FeaturesPlugin_Partition.cpp
src/FeaturesPlugin/FeaturesPlugin_Plugin.cpp
src/FeaturesPlugin/FeaturesPlugin_Union.cpp [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Union.h [new file with mode: 0644]
src/FeaturesPlugin/FeaturesPlugin_Validators.cpp
src/FeaturesPlugin/FeaturesPlugin_Validators.h
src/FeaturesPlugin/Test/TestUnion.py [new file with mode: 0644]
src/FeaturesPlugin/icons/union.png [new file with mode: 0644]
src/FeaturesPlugin/plugin-Features.xml
src/FeaturesPlugin/union_widget.xml [new file with mode: 0644]

index edd823596b23bcd3e6f27d8422f120df1498aeb9..2ffaf860df01f9d6772b9ff0612d091a2daf8ba1 100644 (file)
@@ -24,6 +24,7 @@ SET(PROJECT_HEADERS
     FeaturesPlugin_RevolutionBoolean.h
     FeaturesPlugin_RevolutionCut.h
     FeaturesPlugin_RevolutionFuse.h
+    FeaturesPlugin_Union.h
     FeaturesPlugin_ValidatorTransform.h
     FeaturesPlugin_Validators.h
     FeaturesPlugin_RemoveSubShapes.h
@@ -49,6 +50,7 @@ SET(PROJECT_SOURCES
     FeaturesPlugin_RevolutionBoolean.cpp
     FeaturesPlugin_RevolutionCut.cpp
     FeaturesPlugin_RevolutionFuse.cpp
+    FeaturesPlugin_Union.cpp
     FeaturesPlugin_ValidatorTransform.cpp
     FeaturesPlugin_Validators.cpp
     FeaturesPlugin_RemoveSubShapes.cpp
@@ -71,6 +73,7 @@ SET(XML_RESOURCES
   intersection_widget.xml
   pipe_widget.xml
   remove_subshapes_widget.xml
+  union_widget.xml
 )
 
 SET(TEXT_RESOURCES
index 065e065fc0556781bc527c226bb086f8a0d94cb7..69915b1b38674876b643180cd03e19801d224e8b 100644 (file)
@@ -143,7 +143,7 @@ void FeaturesPlugin_Boolean::execute()
         std::shared_ptr<GeomAPI_Shape> anObject = *anObjectsIt;
         ListOfShape aListWithObject;
         aListWithObject.push_back(anObject);
-        GeomAlgoAPI_MakeShape aBoolAlgo; (aListWithObject, aTools, (GeomAlgoAPI_Boolean::OperationType)aType);
+        GeomAlgoAPI_MakeShape aBoolAlgo;
 
         switch(aType) {
           case BOOL_CUT:    aBoolAlgo = GeomAlgoAPI_Boolean(aListWithObject, aTools, GeomAlgoAPI_Boolean::BOOL_CUT); break;
@@ -346,7 +346,7 @@ void FeaturesPlugin_Boolean::execute()
       } else if((anObjects.size() + aTools.size()) > 1){
         std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects,
                                                                                aTools,
-                                                                               (GeomAlgoAPI_Boolean::OperationType)aType));
+                                                                               GeomAlgoAPI_Boolean::BOOL_FUSE));
 
         // Checking that the algorithm worked properly.
         if(!aFuseAlgo->isDone()) {
index a105bd1db8f71b1d0670cd959411c1d6c0a9aa67..df2ba89ead43bbf7fd90b249c4df4acb721f4c12 100755 (executable)
@@ -143,7 +143,7 @@ void FeaturesPlugin_Partition::storeResult(const ListOfShape& theObjects,
   ResultBodyPtr aResultBody = document()->createBody(data(), theIndex);
 
   // Store modified shape.
-  if(aBaseShape->isEqual(theResultShape)) {
+  if(!aBaseShape.get() || aBaseShape->isEqual(theResultShape)) {
     aResultBody->store(theResultShape);
     setResult(aResultBody, theIndex);
     return;
index d42ede130fe27114837435ce2cd1bad0f8f3148c..de245dfd69dba8cafc2538e3291e20b0fe09be86 100644 (file)
@@ -17,6 +17,7 @@
 #include <FeaturesPlugin_RevolutionCut.h>
 #include <FeaturesPlugin_RevolutionFuse.h>
 #include <FeaturesPlugin_Rotation.h>
+#include <FeaturesPlugin_Union.h>
 #include <FeaturesPlugin_ValidatorTransform.h>
 #include <FeaturesPlugin_Validators.h>
 
@@ -55,6 +56,10 @@ FeaturesPlugin_Plugin::FeaturesPlugin_Plugin()
                               new FeaturesPlugin_ValidatorRemoveSubShapesResult);
   aFactory->registerValidator("FeaturesPlugin_ValidatorPipePath",
                               new FeaturesPlugin_ValidatorPipePath);
+  aFactory->registerValidator("FeaturesPlugin_ValidatorUnionSelection",
+                              new FeaturesPlugin_ValidatorUnionSelection);
+  aFactory->registerValidator("FeaturesPlugin_ValidatorUnionArguments",
+                              new FeaturesPlugin_ValidatorUnionArguments);
 
   // register this plugin
   ModelAPI_Session::get()->registerPlugin(this);
@@ -92,6 +97,8 @@ FeaturePtr FeaturesPlugin_Plugin::createFeature(string theFeatureID)
     return FeaturePtr(new FeaturesPlugin_RevolutionFuse);
   } else if (theFeatureID == FeaturesPlugin_RemoveSubShapes::ID()) {
     return FeaturePtr(new FeaturesPlugin_RemoveSubShapes);
+  } else if (theFeatureID == FeaturesPlugin_Union::ID()) {
+    return FeaturePtr(new FeaturesPlugin_Union);
   }
 
   // feature of such kind is not found
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Union.cpp b/src/FeaturesPlugin/FeaturesPlugin_Union.cpp
new file mode 100644 (file)
index 0000000..1d97c92
--- /dev/null
@@ -0,0 +1,160 @@
+// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
+
+// File:        FeaturesPlugin_Union.cpp
+// Created:     17 June 2016
+// Author:      Dmitry Bobylev
+
+#include "FeaturesPlugin_Union.h"
+
+#include <GeomAlgoAPI_Boolean.h>
+#include <GeomAlgoAPI_MakeShapeList.h>
+#include <GeomAlgoAPI_PaveFiller.h>
+
+#include <GeomAPI_ShapeExplorer.h>
+
+#include <ModelAPI_AttributeSelectionList.h>
+#include <ModelAPI_ResultCompSolid.h>
+#include <ModelAPI_Tools.h>
+
+//=================================================================================================
+FeaturesPlugin_Union::FeaturesPlugin_Union()
+{
+}
+
+//=================================================================================================
+void FeaturesPlugin_Union::initAttributes()
+{
+  data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
+}
+
+//=================================================================================================
+void FeaturesPlugin_Union::execute()
+{
+  ListOfShape anObjects;
+  std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape> aCompSolidsObjects;
+
+  // Getting objects.
+  AttributeSelectionListPtr anObjectsSelList = selectionList(FeaturesPlugin_Union::BASE_OBJECTS_ID());
+  for(int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
+    AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
+    std::shared_ptr<GeomAPI_Shape> anObject = anObjectAttr->value();
+    if(!anObject.get()) {
+      return;
+    }
+    ResultPtr aContext = anObjectAttr->context();
+    ResultCompSolidPtr aResCompSolidPtr = ModelAPI_Tools::compSolidOwner(aContext);
+    if(aResCompSolidPtr.get()) {
+      std::shared_ptr<GeomAPI_Shape> aContextShape = aResCompSolidPtr->shape();
+      std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
+      for(; anIt != aCompSolidsObjects.end(); anIt++) {
+        if(anIt->first->isEqual(aContextShape)) {
+          aCompSolidsObjects[anIt->first].push_back(anObject);
+          break;
+        }
+      }
+      if(anIt == aCompSolidsObjects.end()) {
+        aCompSolidsObjects[aContextShape].push_back(anObject);
+      }
+    } else {
+      anObjects.push_back(anObject);
+    }
+  }
+
+  // Collecting solids from compsolids which will not be modified in boolean operation and will be added to result.
+  ListOfShape aShapesToAdd;
+  for(std::map<std::shared_ptr<GeomAPI_Shape>, ListOfShape>::iterator anIt = aCompSolidsObjects.begin();
+    anIt != aCompSolidsObjects.end(); anIt++) {
+    std::shared_ptr<GeomAPI_Shape> aCompSolid = anIt->first;
+    ListOfShape& aUsedInOperationSolids = anIt->second;
+    anObjects.insert(anObjects.end(), aUsedInOperationSolids.begin(), aUsedInOperationSolids.end());
+
+    // Collect solids from compsolid which will not be modified in boolean operation.
+    for(GeomAPI_ShapeExplorer anExp(aCompSolid, GeomAPI_Shape::SOLID); anExp.more(); anExp.next()) {
+      std::shared_ptr<GeomAPI_Shape> aSolidInCompSolid = anExp.current();
+      ListOfShape::iterator anIt = aUsedInOperationSolids.begin();
+      for(; anIt != aUsedInOperationSolids.end(); anIt++) {
+        if(aSolidInCompSolid->isEqual(*anIt)) {
+          break;
+        }
+      }
+      if(anIt == aUsedInOperationSolids.end()) {
+        aShapesToAdd.push_back(aSolidInCompSolid);
+      }
+    }
+  }
+
+  if(anObjects.size() < 2) {
+    setError("Error: Not enough objects for operation. Should be at least 2.");
+    return;
+  }
+
+  // Fuse objects.
+  ListOfShape aTools;
+  aTools.splice(aTools.begin(), anObjects, anObjects.begin());
+  std::shared_ptr<GeomAlgoAPI_Boolean> aFuseAlgo(new GeomAlgoAPI_Boolean(anObjects,
+                                                                         aTools,
+                                                                         GeomAlgoAPI_Boolean::BOOL_FUSE));
+
+  // Checking that the algorithm worked properly.
+  GeomAlgoAPI_MakeShapeList aMakeShapeList;
+  GeomAPI_DataMapOfShapeShape aMapOfShapes;
+  if(!aFuseAlgo->isDone()) {
+    setError("Error: Boolean algorithm failed.");
+    return;
+  }
+  if(aFuseAlgo->shape()->isNull()) {
+    setError("Error: Resulting shape is Null.");
+    return;
+  }
+  if(!aFuseAlgo->isValid()) {
+    setError("Error: Resulting shape is not valid.");
+    return;
+  }
+
+  GeomShapePtr aShape = aFuseAlgo->shape();
+  aMakeShapeList.appendAlgo(aFuseAlgo);
+  aMapOfShapes.merge(aFuseAlgo->mapOfSubShapes());
+
+  // Store original shapes for naming.
+  anObjects.splice(anObjects.begin(), aTools);
+  anObjects.insert(anObjects.end(), aShapesToAdd.begin(), aShapesToAdd.end());
+
+  // Combine result with not used solids from compsolid.
+  if(aShapesToAdd.size() > 0) {
+    aShapesToAdd.push_back(aShape);
+    std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
+    if(!aFillerAlgo->isDone()) {
+      setError("Error: PaveFiller algorithm failed.");
+      return;
+    }
+    if(aFillerAlgo->shape()->isNull()) {
+      setError("Error: Resulting shape is Null.");
+      return;
+    }
+    if(!aFillerAlgo->isValid()) {
+      setError("Error: Resulting shape is not valid.");
+      return;
+    }
+
+    aShape = aFillerAlgo->shape();
+    aMakeShapeList.appendAlgo(aFillerAlgo);
+    aMapOfShapes.merge(aFillerAlgo->mapOfSubShapes());
+  }
+
+  // Store result and naming.
+  const int aModifyTag = 1;
+  const int aDeletedTag = 2;
+  const int aSubsolidsTag = 3; /// sub solids will be placed at labels 3, 4, etc. if result is compound of solids
+  const std::string aModName = "Modified";
+
+  std::shared_ptr<ModelAPI_ResultBody> aResultBody = document()->createBody(data());
+  aResultBody->storeModified(anObjects.front(), aShape, aSubsolidsTag);
+
+  for(ListOfShape::const_iterator anIter = anObjects.begin(); anIter != anObjects.end(); ++anIter) {
+    aResultBody->loadAndOrientModifiedShapes(&aMakeShapeList, *anIter, GeomAPI_Shape::FACE,
+                                             aModifyTag, aModName, aMapOfShapes);
+    aResultBody->loadDeletedShapes(&aMakeShapeList, *anIter, GeomAPI_Shape::FACE, aDeletedTag);
+  }
+
+  setResult(aResultBody);
+}
diff --git a/src/FeaturesPlugin/FeaturesPlugin_Union.h b/src/FeaturesPlugin/FeaturesPlugin_Union.h
new file mode 100644 (file)
index 0000000..4d993f0
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
+
+// File:        FeaturesPlugin_Union.h
+// Created:     17 June 2016
+// Author:      Dmitry Bobylev
+
+#ifndef FeaturesPlugin_Union_H_
+#define FeaturesPlugin_Union_H_
+
+#include "FeaturesPlugin.h"
+#include <ModelAPI_Feature.h>
+
+#include <GeomAPI_Shape.h>
+
+class GeomAlgoAPI_MakeShape;
+
+/// \class FeaturesPlugin_Union
+/// \ingroup Plugins
+/// \brief Feature for applying of Union operations on Shapes. Union removes shared shapes from
+///        several shapes and combines them into one.
+class FeaturesPlugin_Union : public ModelAPI_Feature
+{
+public:
+  /// Feature kind.
+  inline static const std::string& ID()
+  {
+    static const std::string MY_ID("Union");
+    return MY_ID;
+  }
+
+  /// Attribute name of base objects.
+  inline static const std::string& BASE_OBJECTS_ID()
+  {
+    static const std::string MY_BASE_OBJECTS_ID("base_objects");
+    return MY_BASE_OBJECTS_ID;
+  }
+
+  /// \return the kind of a feature.
+  FEATURESPLUGIN_EXPORT virtual const std::string& getKind()
+  {
+    static std::string MY_KIND = FeaturesPlugin_Union::ID();
+    return MY_KIND;
+  }
+
+  /// Creates a new part document if needed
+  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_Union();
+};
+
+#endif
index eb201778744c54f058e0883b5ce36c5d16efd799..96997a0e0d401e33e3c8fd2b7def7bfc2e6c9d2f 100644 (file)
@@ -6,6 +6,8 @@
 
 #include "FeaturesPlugin_Validators.h"
 
+#include "FeaturesPlugin_Union.h"
+
 #include <ModelAPI_Attribute.h>
 #include <ModelAPI_AttributeInteger.h>
 #include <ModelAPI_AttributeSelectionList.h>
@@ -24,6 +26,7 @@
 #include <GeomAPI_ShapeExplorer.h>
 #include <GeomAPI_ShapeIterator.h>
 
+#include <GeomAlgoAPI_CompoundBuilder.h>
 #include <GeomAlgoAPI_ShapeBuilder.h>
 #include <GeomAlgoAPI_ShapeTools.h>
 #include <GeomAlgoAPI_WireBuilder.h>
@@ -642,3 +645,76 @@ bool FeaturesPlugin_ValidatorRemoveSubShapesResult::isNotObligatory(std::string
 {
   return false;
 }
+
+//==================================================================================================
+bool FeaturesPlugin_ValidatorUnionSelection::isValid(const AttributePtr& theAttribute,
+                                                     const std::list<std::string>& theArguments,
+                                                     std::string& theError) const
+{
+  AttributeSelectionListPtr aBaseObjectsAttrList = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(theAttribute);
+  if(!aBaseObjectsAttrList.get()) {
+    theError = "Error: This validator can only work with selection list in \"" + FeaturesPlugin_Union::ID() + "\" feature.";
+    return false;
+  }
+
+  for(int anIndex = 0; anIndex < aBaseObjectsAttrList->size(); ++anIndex) {
+    bool isSameFound = false;
+    AttributeSelectionPtr anAttrSelectionInList = aBaseObjectsAttrList->value(anIndex);
+    ResultCompSolidPtr aResult = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(anAttrSelectionInList->context());
+    if(!aResult.get()) {
+      continue;
+    }
+    if(aResult->numberOfSubs() > 0) {
+      theError = "Error: Whole compsolids not allowed for selection.";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+//==================================================================================================
+bool FeaturesPlugin_ValidatorUnionArguments::isValid(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                                                     const std::list<std::string>& theArguments,
+                                                     std::string& theError) const
+{
+  // Check feature kind.
+  if(theFeature->getKind() != FeaturesPlugin_Union::ID()) {
+    theError = "Error: This validator supports only \"" + FeaturesPlugin_Union::ID() + "\" feature.";
+    return false;
+  }
+
+  // Get base objects attribute list.
+  AttributeSelectionListPtr aBaseObejctsAttrList = theFeature->selectionList(FeaturesPlugin_Union::BASE_OBJECTS_ID());
+  if(!aBaseObejctsAttrList.get()) {
+    theError = "Error: Could not get \"" + FeaturesPlugin_Union::BASE_OBJECTS_ID() + "\" attribute.";
+    return false;
+  }
+
+  // Get all shapes.
+  ListOfShape aBaseShapesList;
+  for(int anIndex = 0; anIndex < aBaseObejctsAttrList->size(); ++anIndex) {
+    AttributeSelectionPtr anAttrSelectionInList = aBaseObejctsAttrList->value(anIndex);
+    GeomShapePtr aShape = anAttrSelectionInList->value();
+    aBaseShapesList.push_back(aShape);
+  }
+
+  // Make componud and find connected.
+  GeomShapePtr aCompound = GeomAlgoAPI_CompoundBuilder::compound(aBaseShapesList);
+  ListOfShape aCombined, aFree;
+  GeomAlgoAPI_ShapeTools::combineShapes(aCompound, GeomAPI_Shape::COMPSOLID, aCombined, aFree);
+
+  if(aFree.size() > 0 || aCombined.size() > 1) {
+    theError = "Error: Not all shapes have shared topology.";
+    return false;
+  }
+
+  return true;
+}
+
+//==================================================================================================
+bool FeaturesPlugin_ValidatorUnionArguments::isNotObligatory(std::string theFeature,
+                                                             std::string theAttribute)
+{
+  return false;
+}
index da6180e58a7357b8fb4f176e10d5a707dc683b25..1ca14c03aa80458932779d84232c90550377e33c 100644 (file)
@@ -168,4 +168,38 @@ class FeaturesPlugin_ValidatorRemoveSubShapesResult: public ModelAPI_FeatureVali
   virtual bool isNotObligatory(std::string theFeature, std::string theAttribute);
 };
 
+/// \class FeaturesPlugin_ValidatorUnionSelection
+/// \ingroup Validators
+/// \brief Validates selection for "Union" feature.
+class FeaturesPlugin_ValidatorUnionSelection: public ModelAPI_AttributeValidator
+{
+public:
+  /// \return True if the attribute is valid. It checks whether the selection
+  /// is acceptable for 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,
+                       std::string& theError) const;
+};
+
+/// \class FeaturesPlugin_ValidatorUnionArguments
+/// \ingroup Validators
+/// \brief Validator for the "Union" feature.
+class FeaturesPlugin_ValidatorUnionArguments: public ModelAPI_FeatureValidator
+{
+ public:
+  //! \return true if result is valid shape.
+  //! \param theFeature the checked feature
+  //! \param theArguments arguments of the feature (not used)
+  //! \param theError error message
+  virtual bool isValid(const std::shared_ptr<ModelAPI_Feature>& theFeature,
+                       const std::list<std::string>& theArguments,
+                       std::string& theError) const;
+
+  /// \return true if the attribute in feature is not obligatory for the feature execution
+  virtual bool isNotObligatory(std::string theFeature, std::string theAttribute);
+};
+
 #endif
diff --git a/src/FeaturesPlugin/Test/TestUnion.py b/src/FeaturesPlugin/Test/TestUnion.py
new file mode 100644 (file)
index 0000000..562645c
--- /dev/null
@@ -0,0 +1,75 @@
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+from ModelAPI import *
+from GeomDataAPI import *
+from GeomAlgoAPI import *
+from GeomAPI import *
+import math
+
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+
+# Create a part for extrusion
+aSession.startOperation()
+aPartFeature = aDocument.addFeature("Part")
+aSession.finishOperation()
+assert (len(aPartFeature.results()) == 1)
+
+aPartResult = modelAPI_ResultPart(aPartFeature.firstResult())
+aPart = aPartResult.partDoc()
+
+#=========================================================================
+# Create a sketch to extrude
+#=========================================================================
+aSession.startOperation()
+aSketchFeature = featureToCompositeFeature(aPart.addFeature("Sketch"))
+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)
+
+# Create circles
+aSketchCircle = aSketchFeature.addFeature("SketchCircle")
+anCircleCentr = geomDataAPI_Point2D(aSketchCircle.attribute("CircleCenter"))
+aCircleRadius = aSketchCircle.real("CircleRadius")
+anCircleCentr.setValue(-25, 0)
+aCircleRadius.setValue(50)
+aSketchCircle = aSketchFeature.addFeature("SketchCircle")
+anCircleCentr = geomDataAPI_Point2D(aSketchCircle.attribute("CircleCenter"))
+aCircleRadius = aSketchCircle.real("CircleRadius")
+anCircleCentr.setValue(25, 0)
+aCircleRadius.setValue(50)
+aSession.finishOperation()
+aSketchResult = aSketchFeature.firstResult()
+
+#=========================================================================
+# Make extrusion on sketch
+#=========================================================================
+# Create extrusion
+aSession.startOperation()
+anExtrusionFeature = aPart.addFeature("Extrusion")
+anExtrusionFeature.selectionList("base").append(aSketchResult, None)
+anExtrusionFeature.string("CreationMethod").setValue("BySizes")
+anExtrusionFeature.real("to_size").setValue(50)
+anExtrusionFeature.real("from_size").setValue(0)
+anExtrusionFeature.real("to_offset").setValue(0) #TODO: remove
+anExtrusionFeature.real("from_offset").setValue(0) #TODO: remove
+anExtrusionFeature.execute()
+aSession.finishOperation()
+anExtrusionResult = modelAPI_ResultCompSolid(modelAPI_ResultBody(anExtrusionFeature.firstResult()))
+
+#=========================================================================
+# Make union on extrusion
+#=========================================================================
+aSession.startOperation()
+aUnionFeature = aPart.addFeature("Union")
+aUnionFeature.selectionList("base_objects").append(anExtrusionResult.subResult(0), None);
+aUnionFeature.selectionList("base_objects").append(anExtrusionResult.subResult(1), None);
+aUnionFeature.selectionList("base_objects").append(anExtrusionResult.subResult(2), None);
+aSession.finishOperation()
+assert (len(aUnionFeature.results()) > 0)
+anUnionResult = modelAPI_ResultCompSolid(modelAPI_ResultBody(aUnionFeature.firstResult()))
+assert (anUnionResult.numberOfSubs() == 0)
diff --git a/src/FeaturesPlugin/icons/union.png b/src/FeaturesPlugin/icons/union.png
new file mode 100644 (file)
index 0000000..268512c
Binary files /dev/null and b/src/FeaturesPlugin/icons/union.png differ
index 5a70482677608f35c3423cc3242061bb15ba0b6e..6a5cba33d306b83fdd3032f5513a1e6af3eacf4e 100644 (file)
                auto_preview="false">
           <source path="boolean_widget.xml"/>
       </feature>
+      <feature id="Intersection" title="Intersection" tooltip="Intersect objects with tools" icon="icons/Features/intersection.png">
+        <source path="intersection_widget.xml"/>
+      </feature>
       <feature id="Partition" title="Partition" tooltip="Perform partition operations with solids" icon="icons/Features/partition.png"
                auto_preview="false">
           <source path="partition_widget.xml"/>
       </feature>
+      <feature id="Union" title="Union" tooltip="Perform union operations with shapes" icon="icons/Features/union.png">
+          <source path="union_widget.xml"/>
+      </feature>
       <feature id="Remove_SubShapes" title="Remove Sub-Shapes" tooltip="Allows to remove sub-shapes from wires, shells, compsolids and compounds" icon="icons/Features/remove_subshapes.png">
           <source path="remove_subshapes_widget.xml"/>
       </feature>
-      <feature id="Intersection" title="Intersection" tooltip="Intersect objects with tools" icon="icons/Features/intersection.png">
-          <source path="intersection_widget.xml"/>
-      </feature>
     </group>
     <group id="Collections">
       <feature id="Group"
diff --git a/src/FeaturesPlugin/union_widget.xml b/src/FeaturesPlugin/union_widget.xml
new file mode 100644 (file)
index 0000000..f8725f8
--- /dev/null
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
+
+<source>
+  <multi_selector id="base_objects"
+    label="Base objects:"
+    tooltip="Select solids for union."
+    type_choice="solids"
+    concealment="true">
+    <validator id="FeaturesPlugin_ValidatorUnionSelection"/>
+  </multi_selector>
+  <validator id="GeomValidators_MinObjectsSelected" parameters="base_objects,2"/>
+  <validator id="FeaturesPlugin_ValidatorUnionArguments"/>
+</source>