From: jfa Date: Wed, 14 Aug 2024 00:26:42 +0000 (+0100) Subject: [bos #40618] [CEA] Offset/Thickness Feature. X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fjfa%2F40618_Offset;p=modules%2Fgeom.git [bos #40618] [CEA] Offset/Thickness Feature. --- diff --git a/doc/salome/examples/transformation_operations_ex06.py b/doc/salome/examples/transformation_operations_ex06.py index 16bff16e1..3e2ca023d 100644 --- a/doc/salome/examples/transformation_operations_ex06.py +++ b/doc/salome/examples/transformation_operations_ex06.py @@ -13,14 +13,17 @@ box = geompy.MakeBox(20, 20, 20, 200, 200, 200) # create a new object as offset of the given object offset = geompy.MakeOffset(box, 70.) offset2 = geompy.MakeOffsetIntersectionJoin(box, 70.) +offset3 = geompy.MakeOffsetPartial(box, 70., [13, 33]) # add objects in the study id_box = geompy.addToStudy(box, "Box") id_offset = geompy.addToStudy(offset, "Offset") id_offset2 = geompy.addToStudy(offset2, "Offset_intersection_join") +id_offset3 = geompy.addToStudy(offset3, "Offset_Partial") # display the results gg.createAndDisplayGO(id_box) gg.setDisplayMode(id_box,1) gg.createAndDisplayGO(id_offset) gg.createAndDisplayGO(id_offset2) +gg.createAndDisplayGO(id_offset3) diff --git a/doc/salome/gui/GEOM/images/transformation11a.png b/doc/salome/gui/GEOM/images/transformation11a.png new file mode 100644 index 000000000..f9e555bca Binary files /dev/null and b/doc/salome/gui/GEOM/images/transformation11a.png differ diff --git a/doc/salome/gui/GEOM/input/offset_operation.doc b/doc/salome/gui/GEOM/input/offset_operation.doc index d3d1fd197..e75fc12e8 100644 --- a/doc/salome/gui/GEOM/input/offset_operation.doc +++ b/doc/salome/gui/GEOM/input/offset_operation.doc @@ -5,6 +5,10 @@ \n To produce an Offset Surface in the Main Menu select Operations - > Transformation - > Offset Surface +\n There are 2 algorithms for creation of an \b Offset. + +\n Firstly, you can offset all faces of selected shape(s) to the same value. + \n This operation translates each point of an \b Object (a set of Objects) along a local normal by a given \b Offset distance (signed number, negative value meaning inner offset). Gaps between translated @@ -13,12 +17,12 @@ adjacent surfaces are filled in either of two ways: - else the surfaces are extended and intersected, so that sharp edges are preserved. -\n \b Offset operation is applicable to faces, shells and solids. -\n \ref restore_presentation_parameters_page "Advanced options". - - \image html transformation11.png +\n Arguments: Name + Object(s) (face(s), shell(s), solid(s)) + Offset value. +\n The \b Result will be a \b GEOM_Object. +\n \ref restore_presentation_parameters_page "Advanced options". + \n Example: \image html offsetsn.png "The box and its offset surface (Join by pipes activated)" @@ -27,11 +31,27 @@ adjacent surfaces are filled in either of two ways: - Gaps filled by pipes: geompy.MakeOffset(Shape, Offset), - Gaps filled by intersection: geompy.MakeOffsetIntersectionJoin(Shape, Offset), -where Shape is a shape(s) which has to be an offset, Offset is a value of +where \b Shape is a shape(s) which has to be an offset, \b Offset is a value of the offset. -\n Arguments: Name + Object (face(s), shell(s), solid(s)) + -Offset value. + +\n Secondly, you can offset only selected faces of the shape to the given value, + other faces will stay at their initial places, + but could be extended to join with offset faces. + Gaps between adjacent surfaces are filled by intersection of extended surfaces. + +\image html transformation11a.png + +\n Arguments: Name + Object (shell or solid) + + Face(s) of the object + Offset value. \n The \b Result will be a \b GEOM_Object. +\n \ref restore_presentation_parameters_page "Advanced options". + +\n TUI Command: +- geompy.MakeOffsetPartial(Shape, Offset, ListOfFacesIDs), + +where \b Shape is a shape(s) which has to be an offset, + \b Offset is a value of the offset, + \b ListOfFacesIDs is a list of integer IDs of sub-faces. Our TUI Scripts provide you with useful examples of the use of \ref tui_offset "Transformation Operations". diff --git a/idl/GEOM_Gen.idl b/idl/GEOM_Gen.idl index d1428637a..1d6abe8c3 100644 --- a/idl/GEOM_Gen.idl +++ b/idl/GEOM_Gen.idl @@ -1345,6 +1345,18 @@ module GEOM in double theOffset, in boolean theJoinByPipes); + /*! + * \brief Create new object as offset of the given one. + * Only indexed faces are offset, others keep they original location. + * \param theObject The base object for the offset. + * \param theOffset Offset value. + * \param theFacesIDs The list of face IDs indicating faces to be offset. + * \return New GEOM_Object, containing the offset object. + */ + GEOM_Object OffsetShapePartialCopy (in GEOM_Object theObject, + in double theOffset, + in ListOfLong theFacesIDs); + /*! * \brief Create new object as projection of the given one on a 2D surface. * \param theSource The source object for the projection. It can be a point, edge or wire. diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 76bb5fbd7..16985f969 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -132,6 +132,7 @@ SET( _res_files multitranslationsimple.png normale.png offset.png + offset_partial.png projection.png projection_on_edge.png projection_on_wire.png diff --git a/resources/offset_partial.png b/resources/offset_partial.png new file mode 100644 index 000000000..ced1d32df Binary files /dev/null and b/resources/offset_partial.png differ diff --git a/src/GEOMBase/GEOMBase.cxx b/src/GEOMBase/GEOMBase.cxx index d3371cf1f..1071c3e21 100644 --- a/src/GEOMBase/GEOMBase.cxx +++ b/src/GEOMBase/GEOMBase.cxx @@ -822,6 +822,23 @@ QString GEOMBase::GetName( GEOM::GEOM_Object_ptr object ) return name; } +//======================================================================= +// function : GetName() +// purpose : Get name of objects +//======================================================================= +QString GEOMBase::GetName( const QList& objects ) +{ + QString name; + + int nbSel = objects.count(); + if (nbSel == 1) + name = GEOMBase::GetName( objects[0].get() ); + else if (nbSel > 1) + name = QObject::tr("%1_objects").arg( nbSel ); + + return name; +} + //======================================================================= // function : IsShape() // purpose : Return TRUE if object is valid and has shape diff --git a/src/GEOMBase/GEOMBase.h b/src/GEOMBase/GEOMBase.h index c23fe71b2..f7e213551 100644 --- a/src/GEOMBase/GEOMBase.h +++ b/src/GEOMBase/GEOMBase.h @@ -124,6 +124,7 @@ public : /* Gets name of object */ static QString GetName( GEOM::GEOM_Object_ptr object ); + static QString GetName( const QList& objects ); /* Check if object has shape */ static bool IsShape( GEOM::GEOM_Object_ptr object ); diff --git a/src/GEOMGUI/GEOM_images.ts b/src/GEOMGUI/GEOM_images.ts index 03ffd98e9..88abb3e22 100644 --- a/src/GEOMGUI/GEOM_images.ts +++ b/src/GEOMGUI/GEOM_images.ts @@ -451,6 +451,10 @@ ICON_DLG_OFFSET offset.png + + ICON_DLG_OFFSET_PARTIAL + offset_partial.png + ICON_DLG_PROJECTION_ON_FACE projection.png diff --git a/src/GEOMGUI/GEOM_msg_en.ts b/src/GEOMGUI/GEOM_msg_en.ts index 41c3fa5de..c3602d67b 100644 --- a/src/GEOMGUI/GEOM_msg_en.ts +++ b/src/GEOMGUI/GEOM_msg_en.ts @@ -7982,6 +7982,14 @@ Do you want to create new material? GEOM_JOIN_BY_PIPES Join by pipes + + TOOLTIP_OFFSET + Offset + + + TOOLTIP_OFFSET_PARTIAL + Partial offset + OperationGUI_ExtractionDlg diff --git a/src/GEOMGUI/GEOM_msg_fr.ts b/src/GEOMGUI/GEOM_msg_fr.ts index 6605a135e..304834f1c 100644 --- a/src/GEOMGUI/GEOM_msg_fr.ts +++ b/src/GEOMGUI/GEOM_msg_fr.ts @@ -7960,6 +7960,21 @@ Voulez-vous en créer un nouveau ? Longueur de l'angle + + TransformationGUI_OffsetDlg + + GEOM_JOIN_BY_PIPES + Rejoindre par des tuyaux + + + TOOLTIP_OFFSET + Décalage + + + TOOLTIP_OFFSET_PARTIAL + Décalage partiel + + OperationGUI_ExtractionDlg diff --git a/src/GEOMImpl/GEOMImpl_ConformityDriver.cxx b/src/GEOMImpl/GEOMImpl_ConformityDriver.cxx index faed054d0..1ad7c6580 100644 --- a/src/GEOMImpl/GEOMImpl_ConformityDriver.cxx +++ b/src/GEOMImpl/GEOMImpl_ConformityDriver.cxx @@ -42,28 +42,6 @@ namespace { - //======================================================================= - //function : ConvertShapesToIndices - //purpose : Convert sub-shapes of shapes to sequence of indices - //======================================================================= - Handle(TColStd_HArray1OfInteger) ConvertShapesToIndices(const TopoDS_Shape& theShape, - const TopTools_ListOfShape& theShapes) - { - Handle(TColStd_HArray1OfInteger) aSeqOfIDs = new TColStd_HArray1OfInteger(1, theShapes.Size()); - - TopTools_IndexedMapOfShape anIndices; - TopExp::MapShapes(theShape, anIndices); - - TopTools_ListIteratorOfListOfShape itSub(theShapes); - for (int index = 1; itSub.More(); itSub.Next(), ++index) - { - int id = anIndices.FindIndex(itSub.Value()); - aSeqOfIDs->SetValue(index, id); - } - - return aSeqOfIDs; - } - //======================================================================= //function : ConvertShapesToIndices //purpose : Convert list of pair shapes to sequence of indices @@ -203,6 +181,8 @@ Standard_Real GEOMImpl_ConformityDriver::updateTolerance(const TopoDS_Shape& the case TopAbs_FACE: aCurTolerance = BRep_Tool::Tolerance(TopoDS::Face(anExp.Value())); break; + default: + break; } aTolerance = Min(aTolerance, aCurTolerance); } @@ -228,6 +208,8 @@ Standard_Real GEOMImpl_ConformityDriver::updateTolerance(const TopoDS_Shape& the case TopAbs_FACE: aCurTolerance = BRep_Tool::Tolerance(TopoDS::Face(anExp.Value())); break; + default: + break; } aResTol = Max(aResTol, aCurTolerance); } diff --git a/src/GEOMImpl/GEOMImpl_ITransformOperations.cxx b/src/GEOMImpl/GEOMImpl_ITransformOperations.cxx index 4ca0707de..26d3f6005 100644 --- a/src/GEOMImpl/GEOMImpl_ITransformOperations.cxx +++ b/src/GEOMImpl/GEOMImpl_ITransformOperations.cxx @@ -1090,10 +1090,11 @@ GEOMImpl_ITransformOperations::OffsetShape (Handle(GEOM_Object) theObject, * OffsetShapeCopy */ //============================================================================= -Handle(GEOM_Object) -GEOMImpl_ITransformOperations::OffsetShapeCopy( Handle(GEOM_Object) theObject, - double theOffset, - bool theJoinByPipes) +Handle(GEOM_Object) GEOMImpl_ITransformOperations::OffsetShapeCopy + (Handle(GEOM_Object) theObject, + double theOffset, + bool theJoinByPipes, + const Handle(TColStd_HArray1OfInteger)& theFacesIDs) { SetErrorCode(KO); @@ -1118,6 +1119,10 @@ GEOMImpl_ITransformOperations::OffsetShapeCopy( Handle(GEOM_Object) theObject, aTI.SetValue( theOffset ); aTI.SetJoinByPipes( theJoinByPipes ); + if (!theFacesIDs.IsNull()) { + aTI.SetFaceIDs(theFacesIDs); + } + //Compute the offset try { OCC_CATCH_SIGNALS; @@ -1132,18 +1137,31 @@ GEOMImpl_ITransformOperations::OffsetShapeCopy( Handle(GEOM_Object) theObject, } //Make a Python command - if (theJoinByPipes) - GEOM::TPythonDump(aFunction) << aCopy << " = geompy.MakeOffset(" - << theObject << ", " << theOffset << ")"; - else - GEOM::TPythonDump(aFunction) << aCopy << " = geompy.MakeOffsetIntersectionJoin(" - << theObject << ", " << theOffset << ")"; + if (theFacesIDs.IsNull()) { + if (theJoinByPipes) + GEOM::TPythonDump(aFunction) << aCopy << " = geompy.MakeOffset(" + << theObject << ", " << theOffset << ")"; + else + GEOM::TPythonDump(aFunction) << aCopy << " = geompy.MakeOffsetIntersectionJoin(" + << theObject << ", " << theOffset << ")"; + } + else { + GEOM::TPythonDump pd (aFunction); + pd << aCopy << " = geompy.MakeOffsetPartial(" + << theObject << ", " << theOffset << ", ["; + + // Dump faces IDs. + for (Standard_Integer i = theFacesIDs->Lower(); i <= theFacesIDs->Upper(); ++i) { + pd << theFacesIDs->Value(i) << ((i == theFacesIDs->Upper()) ? "" : ", "); + } + + pd << "])"; + } SetErrorCode(OK); return aCopy; } - //============================================================================= /*! * ProjectShapeCopy diff --git a/src/GEOMImpl/GEOMImpl_ITransformOperations.hxx b/src/GEOMImpl/GEOMImpl_ITransformOperations.hxx index 25498ce00..aac62cfdd 100644 --- a/src/GEOMImpl/GEOMImpl_ITransformOperations.hxx +++ b/src/GEOMImpl/GEOMImpl_ITransformOperations.hxx @@ -108,9 +108,11 @@ class GEOMImpl_ITransformOperations : public GEOM_IOperations double theOffset, bool theJoinByPipes); - Standard_EXPORT Handle(GEOM_Object) OffsetShapeCopy (Handle(GEOM_Object) theObject, - double theOffset, - bool theJoinByPipes); + Standard_EXPORT Handle(GEOM_Object) OffsetShapeCopy + (Handle(GEOM_Object) theObject, + double theOffset, + bool theJoinByPipes, + const Handle(TColStd_HArray1OfInteger)& theFacesIDs = NULL); Standard_EXPORT Handle(GEOM_Object) ProjectShapeCopy (Handle(GEOM_Object) theSource, Handle(GEOM_Object) theTarget); diff --git a/src/GEOMImpl/GEOMImpl_OffsetDriver.cxx b/src/GEOMImpl/GEOMImpl_OffsetDriver.cxx index f33b86721..fb47d9e4e 100644 --- a/src/GEOMImpl/GEOMImpl_OffsetDriver.cxx +++ b/src/GEOMImpl/GEOMImpl_OffsetDriver.cxx @@ -28,12 +28,13 @@ #include #include +#include #include #include -#include #include #include -#include +#include +#include #include #include #include @@ -88,24 +89,79 @@ Standard_Integer GEOMImpl_OffsetDriver::Execute(Handle(TFunction_Logbook)& log) if ( aType == OFFSET_SHAPE || aType == OFFSET_SHAPE_COPY ) { - BRepOffsetAPI_MakeOffsetShape MO; BRepOffset_Mode aMode = BRepOffset_Skin; - Standard_Boolean anIntersection = Standard_False, aSelfInter = Standard_False; - MO.PerformByJoin( aShapeBase, - aCI.GetValue(), - aTol, - aMode, - anIntersection, - aSelfInter, - aCI.GetJoinByPipes() ? GeomAbs_Arc : GeomAbs_Intersection ); - - if ( MO.IsDone() ) { - aShape = MO.Shape(); - if ( !GEOMUtils::CheckShape(aShape, true) && !GEOMUtils::FixShapeTolerance(aShape) ) - Standard_ConstructionError::Raise("Boolean operation aborted : non valid shape result"); + Standard_Boolean anIntersection = Standard_False; + Standard_Boolean aSelfInter = Standard_False; + + Handle(TColStd_HArray1OfInteger) aFacesIDs = aCI.GetFaceIDs(); + if (aFacesIDs.IsNull() || aFacesIDs->Length() < 1) { + // Offset entire shape (all faces) with the same offset value + BRepOffsetAPI_MakeOffsetShape MO; + MO.PerformByJoin( aShapeBase, + anOffset, + aTol, + aMode, + anIntersection, + aSelfInter, + aCI.GetJoinByPipes() ? GeomAbs_Arc : GeomAbs_Intersection ); + + if ( MO.IsDone() ) { + aShape = MO.Shape(); + if ( !GEOMUtils::CheckShape(aShape, true) && !GEOMUtils::FixShapeTolerance(aShape) ) + Standard_ConstructionError::Raise("Offset aborted : non valid shape result"); + } + else { + StdFail_NotDone::Raise("Offset construction failed"); + } } else { - StdFail_NotDone::Raise("Offset construction failed"); + // Offset selected faces of main shape by given value, other faces by 0 + BRepOffset_MakeOffset aMakeOffset; + aMakeOffset.Initialize(aShapeBase, + anOffset, // set offset on all faces to anOffset + aTol, + aMode, + anIntersection, + aSelfInter, + aCI.GetJoinByPipes() ? GeomAbs_Arc : GeomAbs_Intersection, + Standard_False); + + // put selected faces into a map + TopTools_MapOfShape aMapFaces; + TopTools_IndexedMapOfShape anIndices; + TopExp::MapShapes(aShapeBase, anIndices); + Standard_Integer aNbShapes = anIndices.Extent(); + for (Standard_Integer i = aFacesIDs->Lower(); i <= aFacesIDs->Upper(); ++i) { + const Standard_Integer anIndex = aFacesIDs->Value(i); + if (anIndex < 1 || anIndex > aNbShapes) { + Standard_ConstructionError::Raise("Offset aborted : Invalid face index given"); + } + const TopoDS_Shape &aFace = anIndices.FindKey(anIndex); + if (aFace.ShapeType() != TopAbs_FACE) { + Standard_ConstructionError::Raise("Offset aborted : Shape by index is not a face"); + } + aMapFaces.Add(aFace); + } + + // set offset on non-selected faces to zero + TopExp_Explorer anExp (aShapeBase, TopAbs_FACE); + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &aFace = anExp.Current(); + if (!aMapFaces.Contains(aFace)) { + aMakeOffset.SetOffsetOnFace(TopoDS::Face(aFace), 0.0); + } + } + + // perform offset operation + aMakeOffset.MakeOffsetShape(); + if ( aMakeOffset.IsDone() ) { + aShape = aMakeOffset.Shape(); + if ( !GEOMUtils::CheckShape(aShape, true) && !GEOMUtils::FixShapeTolerance(aShape) ) + Standard_ConstructionError::Raise("Offset aborted : non valid shape result"); + } + else { + StdFail_NotDone::Raise("Offset construction failed"); + } } } else if (aType == OFFSET_THICKENING || aType == OFFSET_THICKENING_COPY) @@ -216,6 +272,12 @@ GetCreationInformation(std::string& theOperationName, theOperationName = "OFFSET"; AddParam( theParams, "Object", aCI.GetShape() ); AddParam( theParams, "Offset", aCI.GetValue() ); + { + Handle(TColStd_HArray1OfInteger) aFacesIDs = aCI.GetFaceIDs(); + if (!aFacesIDs.IsNull()) { + AddParam(theParams, "Faces IDs", aFacesIDs); + } + } break; case OFFSET_THICKENING: case OFFSET_THICKENING_COPY: diff --git a/src/GEOM_I/GEOM_IMeasureOperations_i.cc b/src/GEOM_I/GEOM_IMeasureOperations_i.cc index 3c7d6343f..0acbdb36c 100644 --- a/src/GEOM_I/GEOM_IMeasureOperations_i.cc +++ b/src/GEOM_I/GEOM_IMeasureOperations_i.cc @@ -1366,8 +1366,8 @@ GEOM::GEOM_IMeasureOperations::SequenceOfPairOfShape* GEOM_IMeasureOperations_i: std::list::iterator anIter(aSelfInters.begin()); for (Standard_Integer i = 0; i < aLength; i++, ++anIter) { - aSeq[i].first = GetObject(Handle(::GEOM_Object)::DownCast((*anIter).first)); - aSeq[i].second = GetObject(Handle(::GEOM_Object)::DownCast((*anIter).second)); + aSeq[i].first = GetObject((*anIter).first); + aSeq[i].second = GetObject((*anIter).second); } return aSeq._retn(); @@ -1404,8 +1404,8 @@ GEOM::GEOM_IMeasureOperations::SequenceOfPairOfShape* GEOM_IMeasureOperations_i: std::list::iterator anIter(aSelfInterf.begin()); for (Standard_Integer i = 0; i < aLength; i++, ++anIter) { - aSeq[i].first = GetObject(Handle(::GEOM_Object)::DownCast((*anIter).first)); - aSeq[i].second = GetObject(Handle(::GEOM_Object)::DownCast((*anIter).second)); + aSeq[i].first = GetObject((*anIter).first); + aSeq[i].second = GetObject((*anIter).second); } return aSeq._retn(); @@ -1471,8 +1471,8 @@ GEOM::GEOM_IMeasureOperations::SequenceOfPairOfShape* GEOM_IMeasureOperations_i: std::list::iterator anIter(aDistantS.begin()); for (Standard_Integer i = 0; i < aLength; i++, ++anIter) { - aSeq[i].first = GetObject(Handle(::GEOM_Object)::DownCast((*anIter).first)); - aSeq[i].second = GetObject(Handle(::GEOM_Object)::DownCast((*anIter).second)); + aSeq[i].first = GetObject((*anIter).first); + aSeq[i].second = GetObject((*anIter).second); } return aSeq._retn(); @@ -1502,8 +1502,8 @@ GEOM::GEOM_IMeasureOperations::CheckResults* GEOM_IMeasureOperations_i::CheckCon for (Standard_Integer i = 0; i < aLength; i++, ++anIntIt) { aRes[i].type = (*anIntIt).TypeOfCheck; - aRes[i].failedShapes.first = GetObject(Handle(::GEOM_Object)::DownCast((*anIntIt).FailedShapes.first)); - aRes[i].failedShapes.second = GetObject(Handle(::GEOM_Object)::DownCast((*anIntIt).FailedShapes.second)); + aRes[i].failedShapes.first = GetObject((*anIntIt).FailedShapes.first); + aRes[i].failedShapes.second = GetObject((*anIntIt).FailedShapes.second); } return aRes._retn(); @@ -1530,7 +1530,7 @@ CORBA::Double GEOM_IMeasureOperations_i::UpdateTolerance(GEOM::GEOM_Object_ptr t void GEOM_IMeasureOperations_i::ConvertToList(const GEOM::GEOM_IMeasureOperations::CheckResults& theResuts, std::list& theListOfResults) { - for (Standard_Integer i = 0; i < theResuts.length(); ++i) + for (size_t i = 0; i < theResuts.length(); ++i) { GEOMImpl_IMeasureOperations::FailedChecks aCheck; aCheck.TypeOfCheck = theResuts[i].type; diff --git a/src/GEOM_I/GEOM_ITransformOperations_i.cc b/src/GEOM_I/GEOM_ITransformOperations_i.cc index 8f866ca41..c0476d415 100644 --- a/src/GEOM_I/GEOM_ITransformOperations_i.cc +++ b/src/GEOM_I/GEOM_ITransformOperations_i.cc @@ -668,6 +668,47 @@ GEOM_ITransformOperations_i::OffsetShapeCopy (GEOM::GEOM_Object_ptr theObject, return GetObject(anObject); } +//============================================================================= +/*! + * OffsetShapePartialCopy + */ +//============================================================================= +GEOM::GEOM_Object_ptr +GEOM_ITransformOperations_i::OffsetShapePartialCopy (GEOM::GEOM_Object_ptr theObject, + CORBA::Double theOffset, + const GEOM::ListOfLong& theFacesIDs) +{ + GEOM::GEOM_Object_var aGEOMObject; + + //Set a not done flag + GetOperations()->SetNotDone(); + + //Get the basic object + Handle(::GEOM_Object) aBasicObject = GetObjectImpl(theObject); + if (aBasicObject.IsNull()) return aGEOMObject._retn(); + + // Get faces IDs. + Handle(TColStd_HArray1OfInteger) aFaceIDs; + Standard_Integer aNbIDs = theFacesIDs.length(); + if (aNbIDs > 0) { + aFaceIDs = new TColStd_HArray1OfInteger (1, aNbIDs); + for (Standard_Integer i = 0; i < aNbIDs; i++) { + aFaceIDs->SetValue(i + 1, theFacesIDs[i]); + } + } + + // join by pipes mode is not supported in combination with partial offset + bool aJoinByPipes = false; + + //Create the offset shape + Handle(::GEOM_Object) anObject = + GetOperations()->OffsetShapeCopy(aBasicObject, theOffset, aJoinByPipes, aFaceIDs); + if (!GetOperations()->IsDone() || anObject.IsNull()) + return aGEOMObject._retn(); + + return GetObject(anObject); +} + //============================================================================= /*! * ProjectShapeCopy diff --git a/src/GEOM_I/GEOM_ITransformOperations_i.hh b/src/GEOM_I/GEOM_ITransformOperations_i.hh index d4ad3f00f..66f9983da 100644 --- a/src/GEOM_I/GEOM_ITransformOperations_i.hh +++ b/src/GEOM_I/GEOM_ITransformOperations_i.hh @@ -142,6 +142,10 @@ class GEOM_I_EXPORT GEOM_ITransformOperations_i : CORBA::Double theOffset, CORBA::Boolean theJoinByPipes); + GEOM::GEOM_Object_ptr OffsetShapePartialCopy (GEOM::GEOM_Object_ptr theObject, + CORBA::Double theOffset, + const GEOM::ListOfLong& theFacesIDs); + GEOM::GEOM_Object_ptr ProjectShapeCopy (GEOM::GEOM_Object_ptr theSource, GEOM::GEOM_Object_ptr theTarget); diff --git a/src/GEOM_SWIG/geomBuilder.py b/src/GEOM_SWIG/geomBuilder.py index 7cf3edc64..e9cf78b47 100644 --- a/src/GEOM_SWIG/geomBuilder.py +++ b/src/GEOM_SWIG/geomBuilder.py @@ -9623,6 +9623,51 @@ class geomBuilder(GEOM._objref_GEOM_Gen): self._autoPublish(anObj, theName, "offset") return anObj + ## Create new object as partial offset of the given one. + # Only indexed faces are offset, others keep they original location. + # Gap between adjacent offset surfaces is filled + # by extending and intersecting them. + # @param theObject The base object for the offset. + # @param theOffset Offset value. + # @param theFacesIDs The list of face IDs indicating faces to be offset. + # @param theName Object name; when specified, this parameter is used + # for result publication in the study. Otherwise, if automatic + # publication is switched on, default value is used for result name. + # + # @return New GEOM.GEOM_Object, containing the offset object. + # + # @ref tui_offset "Example" + @ManageTransactions("TrsfOp") + def MakeOffsetPartial(self, theObject, theOffset, theFacesIDs, theName=None): + """ + Create new object as partial offset of the given one. + Only indexed faces are offset, others keep they original location. + Gap between adjacent offset surfaces is filled + by extending and intersecting them. + + Parameters: + theObject The base object for the offset. + theOffset Offset value. + theFacesIDs The list of face IDs indicating faces to be offset. + theName Object name; when specified, this parameter is used + for result publication in the study. Otherwise, if automatic + publication is switched on, default value is used for result name. + + Returns: + New GEOM.GEOM_Object, containing the offset object. + + Example of usage: + box = geompy.MakeBox(20, 20, 20, 200, 200, 200) + # create a new object from the box, offsetting its top and front faces + offset = geompy.MakeOffsetPartial(box, 70., [13, 33]) + """ + theOffset, Parameters = ParseParameters(theOffset) + anObj = self.TrsfOp.OffsetShapePartialCopy(theObject, theOffset, theFacesIDs) + RaiseIfFailed("OffsetShapePartialCopy", self.TrsfOp) + anObj.SetParameters(Parameters) + self._autoPublish(anObj, theName, "offset") + return anObj + ## Create new object as projection of the given one on another. # @param theSource The source object for the projection. It can be a point, edge or wire. # Edge and wire are acceptable if @a theTarget is a face. diff --git a/src/TransformationGUI/TransformationGUI_OffsetDlg.cxx b/src/TransformationGUI/TransformationGUI_OffsetDlg.cxx index 523c7b62f..d257792ad 100644 --- a/src/TransformationGUI/TransformationGUI_OffsetDlg.cxx +++ b/src/TransformationGUI/TransformationGUI_OffsetDlg.cxx @@ -35,6 +35,9 @@ #include #include +#include +#include + //================================================================================= // class : TransformationGUI_OffsetDlg() // purpose : Constructs a TransformationGUI_OffsetDlg which is a child of 'parent', with the @@ -42,31 +45,43 @@ // The dialog will by default be modeless, unless you set 'modal' to // TRUE to construct a modal dialog. //================================================================================= -TransformationGUI_OffsetDlg::TransformationGUI_OffsetDlg( GeometryGUI* theGeometryGUI, QWidget* parent, - bool modal, Qt::WindowFlags fl ) - : GEOMBase_Skeleton( theGeometryGUI, parent, modal, fl ) +TransformationGUI_OffsetDlg::TransformationGUI_OffsetDlg + (GeometryGUI* theGeometryGUI, QWidget* parent, + bool modal, Qt::WindowFlags fl) + : GEOMBase_Skeleton(theGeometryGUI, parent, modal, fl) { - QPixmap image0( SUIT_Session::session()->resourceMgr()->loadPixmap( "GEOM", tr( "ICON_DLG_OFFSET" ) ) ); - QPixmap image1( SUIT_Session::session()->resourceMgr()->loadPixmap( "GEOM", tr( "ICON_SELECT" ) ) ); + SUIT_ResourceMgr* aResMgr = SUIT_Session::session()->resourceMgr(); + QPixmap image0 (aResMgr->loadPixmap("GEOM", tr("ICON_SELECT"))); + QPixmap image1 (aResMgr->loadPixmap("GEOM", tr("ICON_DLG_OFFSET"))); + QPixmap image2 (aResMgr->loadPixmap("GEOM", tr("ICON_DLG_OFFSET_PARTIAL"))); - setWindowTitle( tr( "GEOM_OFFSET_TITLE" ) ); + setWindowTitle(tr("GEOM_OFFSET_TITLE")); /***************************************************************/ - mainFrame()->GroupConstructors->setTitle( tr( "GEOM_OFFSET" ) ); - mainFrame()->RadioButton1->setIcon( image0 ); - mainFrame()->RadioButton2->setAttribute( Qt::WA_DeleteOnClose ); - mainFrame()->RadioButton2->close(); - mainFrame()->RadioButton3->setAttribute( Qt::WA_DeleteOnClose ); + mainFrame()->GroupConstructors->setTitle(tr("GEOM_OFFSET")); + mainFrame()->RadioButton1->setIcon(image1); + mainFrame()->RadioButton2->setIcon(image2); + mainFrame()->RadioButton1->setToolTip(tr("TOOLTIP_OFFSET")); + mainFrame()->RadioButton2->setToolTip(tr("TOOLTIP_OFFSET_PARTIAL")); + mainFrame()->RadioButton3->setAttribute(Qt::WA_DeleteOnClose); mainFrame()->RadioButton3->close(); + mainFrame()->RadioButton1->setChecked(true); + + GroupPoints = new DlgRef_2Sel1Spin2Check( centralWidget() ); - GroupPoints = new DlgRef_1Sel1Spin1Check( centralWidget() ); GroupPoints->GroupBox1->setTitle( tr( "GEOM_ARGUMENTS" ) ); + GroupPoints->TextLabel1->setText( tr( "GEOM_OBJECTS" ) ); - GroupPoints->TextLabel2->setText( tr( "GEOM_OFFSET" ) ); + GroupPoints->TextLabel2->setText( tr( "GEOM_FACES" ) ); + GroupPoints->TextLabel3->setText( tr( "GEOM_OFFSET" ) ); + + GroupPoints->PushButton1->setIcon( image0 ); + GroupPoints->PushButton2->setIcon( image0 ); + GroupPoints->CheckButton1->setText( tr( "GEOM_JOIN_BY_PIPES" ) ); GroupPoints->CheckButton1->setChecked( true ); - - GroupPoints->PushButton1->setIcon( image1 ); + GroupPoints->CheckButton2->setAttribute( Qt::WA_DeleteOnClose ); + GroupPoints->CheckButton2->close(); QVBoxLayout* layout = new QVBoxLayout( centralWidget() ); layout->setMargin( 0 ); layout->setSpacing( 6 ); @@ -99,33 +114,76 @@ void TransformationGUI_OffsetDlg::Init() /* init variables */ myEditCurrentArgument = GroupPoints->LineEdit1; GroupPoints->LineEdit1->setReadOnly( true ); + GroupPoints->LineEdit2->setReadOnly( true ); myObjects.clear(); - - /* Get setting of step value from file configuration */ - double step = 1; + myFaces.clear(); /* min, max, step and decimals for spin boxes & initial values */ + double step = 1; initSpinBox( GroupPoints->SpinBox_DX, COORD_MIN, COORD_MAX, step, "length_precision" ); GroupPoints->SpinBox_DX->setValue( 1e-05 ); mainFrame()->GroupBoxPublish->show(); /* signals and slots connections */ - connect( buttonOk(), SIGNAL( clicked() ), this, SLOT( ClickOnOk() ) ); - connect( buttonApply(), SIGNAL( clicked() ), this, SLOT( ClickOnApply() ) ); + connect(buttonOk(), SIGNAL(clicked()), this, SLOT(ClickOnOk())); + connect(buttonApply(), SIGNAL(clicked()), this, SLOT(ClickOnApply())); + connect(this, SIGNAL(constructorsClicked(int)), + this, SLOT(ConstructorsClicked(int))); - connect( GroupPoints->PushButton1, SIGNAL( clicked() ), this, SLOT( SetEditCurrentArgument() ) ); - connect( myGeomGUI->getApp()->selectionMgr(), - SIGNAL( currentSelectionChanged() ), this, SLOT( SelectionIntoArgument() ) ); + connect(GroupPoints->PushButton1, SIGNAL(clicked()), + this, SLOT(SetEditCurrentArgument())); + connect(GroupPoints->PushButton2, SIGNAL(clicked()), + this, SLOT(SetEditCurrentArgument())); - connect( GroupPoints->SpinBox_DX, SIGNAL( valueChanged( double ) ), this, SLOT( ValueChangedInSpinBox() ) ); - connect( GroupPoints->CheckButton1, SIGNAL( toggled( bool ) ), this, SLOT( JoinModeChanged() ) ); + connect(GroupPoints->SpinBox_DX, SIGNAL(valueChanged(double)), + this, SLOT(ValueChangedInSpinBox())); + connect(GroupPoints->CheckButton1, SIGNAL(toggled(bool)), + this, SLOT(JoinModeChanged())); - initName( tr( "GEOM_OFFSET" ) ); + connect(myGeomGUI->getApp()->selectionMgr(), SIGNAL(currentSelectionChanged()), + this, SLOT(SelectionIntoArgument())); - globalSelection( GEOM_ALLSHAPES ); - resize(100,100); + initName(tr("GEOM_OFFSET")); + + ConstructorsClicked(0); +} + +//============================================================================== +// function : ConstructorsClicked() +// purpose : Radio button management +//============================================================================== +void TransformationGUI_OffsetDlg::ConstructorsClicked (int constructorId) +{ + switch (constructorId) { + case 0: + // disable faces selection + GroupPoints->TextLabel2->hide(); + GroupPoints->PushButton2->hide(); + GroupPoints->LineEdit2->hide(); + + // enable joint type + GroupPoints->CheckButton1->show(); + break; + case 1: + // enable faces selection + GroupPoints->TextLabel2->show(); + GroupPoints->PushButton2->show(); + GroupPoints->LineEdit2->show(); + + // disable joint type + GroupPoints->CheckButton1->hide(); + break; + default: + break; + } + + qApp->processEvents(); + updateGeometry(); + resize(minimumSizeHint()); + + GroupPoints->PushButton1->click(); SelectionIntoArgument(); } @@ -152,6 +210,10 @@ bool TransformationGUI_OffsetDlg::ClickOnApply() return false; initName(); + + // activate selection and connect selection manager + ConstructorsClicked(getConstructorId()); + return true; } @@ -162,13 +224,27 @@ bool TransformationGUI_OffsetDlg::ClickOnApply() //================================================================================= void TransformationGUI_OffsetDlg::SelectionIntoArgument() { - myObjects = getSelected( TopAbs_SHAPE, -1 ); - if ( !myObjects.isEmpty() ) { - QString aName = myObjects.count() > 1 ? QString( "%1_objects").arg( myObjects.count() ) : GEOMBase::GetName( myObjects[0].get() ); - myEditCurrentArgument->setText( aName ); + myEditCurrentArgument->setText(""); + + if (myEditCurrentArgument == GroupPoints->LineEdit1) { + if (getConstructorId() == 0) { + myObjects = getSelected( TopAbs_SHAPE, -1 ); + myEditCurrentArgument->setText(GEOMBase::GetName(myObjects)); + } + else if (getConstructorId() == 1) { + myFaces.clear(); + GroupPoints->LineEdit2->setText(""); + myObjects = getSelected(TopAbs_SHAPE, 1); // only one object allowed + if (!myObjects.isEmpty()) { + myEditCurrentArgument->setText(GEOMBase::GetName(myObjects[0].get())); + GroupPoints->PushButton2->click(); + } + } } - else { - myEditCurrentArgument->setText(""); + else if (myEditCurrentArgument == GroupPoints->LineEdit2) { + myFaces.clear(); + myFaces = getSelected(TopAbs_FACE, -1); + myEditCurrentArgument->setText(GEOMBase::GetName(myFaces)); } processPreview(); @@ -184,11 +260,20 @@ void TransformationGUI_OffsetDlg::SetEditCurrentArgument() QPushButton* send = (QPushButton*)sender(); if ( send == GroupPoints->PushButton1 ) { - GroupPoints->PushButton1->setDown(true); + GroupPoints->PushButton2->setDown(false); myEditCurrentArgument = GroupPoints->LineEdit1; - myEditCurrentArgument->setFocus(); - SelectionIntoArgument(); + globalSelection(GEOM_ALLSHAPES); } + else if (send == GroupPoints->PushButton2) { + GroupPoints->PushButton1->setDown(false); + myEditCurrentArgument = GroupPoints->LineEdit2; + globalSelection(); // close local contexts, if any + if (myObjects.size() > 0) + localSelection(myObjects[0].get(), TopAbs_FACE); + } + + myEditCurrentArgument->setFocus(); + send->setDown(true); } @@ -210,11 +295,11 @@ void TransformationGUI_OffsetDlg::enterEvent( QEvent* ) void TransformationGUI_OffsetDlg::ActivateThisDialog() { GEOMBase_Skeleton::ActivateThisDialog(); + connect( myGeomGUI->getApp()->selectionMgr(), SIGNAL( currentSelectionChanged() ), this, SLOT( SelectionIntoArgument() ) ); - globalSelection( GEOM_ALLSHAPES ); - myEditCurrentArgument = GroupPoints->LineEdit1; - myEditCurrentArgument->setFocus(); + + ConstructorsClicked(getConstructorId()); } @@ -243,14 +328,27 @@ GEOM::GEOM_IOperations_ptr TransformationGUI_OffsetDlg::createOperation() //================================================================================= bool TransformationGUI_OffsetDlg::isValid( QString& msg ) { - bool ok = GroupPoints->SpinBox_DX->isValid( msg, !IsPreview() ) && !myObjects.isEmpty(); - for ( int i = 0; i < myObjects.count() && ok; i++ ) { - GEOM::shape_type aType = myObjects[i]->GetShapeType(); - ok = aType == GEOM::FACE || aType == GEOM::SHELL || aType == GEOM::SOLID; - if ( !ok ) - msg = tr( "ERROR_SHAPE_TYPE" ); + bool isOk = GroupPoints->SpinBox_DX->isValid( msg, !IsPreview() ) && !myObjects.isEmpty(); + + if (isOk) { + switch (getConstructorId()) { + case 0: + for ( int i = 0; i < myObjects.count() && isOk; i++ ) { + GEOM::shape_type aType = myObjects[i]->GetShapeType(); + isOk = aType == GEOM::FACE || aType == GEOM::SHELL || aType == GEOM::SOLID; + if ( !isOk ) + msg = tr( "ERROR_SHAPE_TYPE" ); + } + break; + case 1: + isOk = myObjects.size() == 1 && !myFaces.isEmpty(); + break; + default: + break; + } } - return ok; + + return isOk; } //================================================================================= @@ -263,28 +361,59 @@ bool TransformationGUI_OffsetDlg::execute( ObjectList& objects ) GEOM::GEOM_Object_var anObj; - GEOM::GEOM_ITransformOperations_var anOper = GEOM::GEOM_ITransformOperations::_narrow(getOperation()); + GEOM::GEOM_ITransformOperations_var anOper = + GEOM::GEOM_ITransformOperations::_narrow(getOperation()); - if ( true /*GroupPoints->CheckButton1->isChecked() || IsPreview()*/ ) { - for ( int i = 0; i < myObjects.count(); i++ ) { - - anObj = anOper->OffsetShapeCopy( myObjects[i].get(), GetOffset(), GetIsJoinByPipes() ); - if ( !anObj->_is_nil() ) { - if(!IsPreview()) { + if (getConstructorId() == 0) { + for (int i = 0; i < myObjects.count(); i++) { + anObj = anOper->OffsetShapeCopy(myObjects[i].get(), GetOffset(), GetIsJoinByPipes()); + if (!anObj->_is_nil()) { + if (!IsPreview()) { anObj->SetParameters(GroupPoints->SpinBox_DX->text().toUtf8().constData()); } - objects.push_back( anObj._retn() ); + objects.push_back(anObj._retn()); + res = true; } } } - else { - for ( int i = 0; i < myObjects.count(); i++ ) { - anObj = anOper->OffsetShape( myObjects[i].get(), GetOffset(), GetIsJoinByPipes() ); - if ( !anObj->_is_nil() ) - objects.push_back( anObj._retn() ); + else if (getConstructorId() == 1) { + if (myObjects.count() == 1) { + TopoDS_Shape aShape; + if (GEOMBase::GetShape(myObjects[0].get(), aShape)) { + TopTools_IndexedMapOfShape aMainMap; + TopExp::MapShapes(aShape, aMainMap); + + QList aListIDs; + for (int i = 0; i < myFaces.count(); i++) { + TopoDS_Shape aFace; + if (GEOMBase::GetShape(myFaces[i].get(), aFace)) { + int anIndex = aMainMap.FindIndex(aFace); + if (anIndex >= 0) { + aListIDs << anIndex; + } + } + } + + GEOM::ListOfLong_var aFacesIDsList = new GEOM::ListOfLong(); + if (!aListIDs.empty()) { + aFacesIDsList->length(aListIDs.length()); + for (int i = 0; i < aListIDs.length(); i++) { + aFacesIDsList[i] = aListIDs[i]; + } + } + + anObj = anOper->OffsetShapePartialCopy + (myObjects[0].get(), GetOffset(), aFacesIDsList); + if (!anObj->_is_nil()) { + if (!IsPreview()) { + anObj->SetParameters(GroupPoints->SpinBox_DX->text().toUtf8().constData()); + } + objects.push_back(anObj._retn()); + res = true; + } + } } } - res = true; return res; } @@ -330,7 +459,6 @@ bool TransformationGUI_OffsetDlg::GetIsJoinByPipes() const void TransformationGUI_OffsetDlg::JoinModeChanged() { processPreview(); - //mainFrame()->GroupBoxName->setEnabled( GroupPoints->CheckButton1->isChecked() ); } //================================================================================= diff --git a/src/TransformationGUI/TransformationGUI_OffsetDlg.h b/src/TransformationGUI/TransformationGUI_OffsetDlg.h index b23938044..9407a3e78 100644 --- a/src/TransformationGUI/TransformationGUI_OffsetDlg.h +++ b/src/TransformationGUI/TransformationGUI_OffsetDlg.h @@ -30,14 +30,14 @@ #include "GEOMBase_Skeleton.h" #include "GEOM_GenericObjPtr.h" -class DlgRef_1Sel1Spin1Check; - +class DlgRef_2Sel1Spin2Check; + //================================================================================= // class : TransformationGUI_OffsetDlg // purpose : //================================================================================= class TransformationGUI_OffsetDlg : public GEOMBase_Skeleton -{ +{ Q_OBJECT public: @@ -52,24 +52,26 @@ protected: virtual bool execute( ObjectList& ); virtual void restoreSubShapes( SALOMEDS::SObject_ptr ); virtual QList getSourceObjects(); - + private: void Init(); void enterEvent( QEvent* ); double GetOffset() const; bool GetIsJoinByPipes() const; - + private: QList myObjects; - - DlgRef_1Sel1Spin1Check* GroupPoints; - + QList myFaces; + + DlgRef_2Sel1Spin2Check* GroupPoints; + private slots: void ClickOnOk(); bool ClickOnApply(); void ActivateThisDialog(); void SelectionIntoArgument(); void SetEditCurrentArgument(); + void ConstructorsClicked(int); void ValueChangedInSpinBox(); void JoinModeChanged(); };