From 67feb126ffd2576ffed52986c4906be13db2371d Mon Sep 17 00:00:00 2001 From: jfa Date: Wed, 14 Aug 2024 01:26:42 +0100 Subject: [PATCH] [bos #40618] [CEA] Offset/Thickness Feature. --- .../transformation_operations_ex06.py | 3 + .../gui/GEOM/images/transformation11a.png | Bin 0 -> 13585 bytes .../gui/GEOM/input/offset_operation.doc | 34 ++- idl/GEOM_Gen.idl | 12 + resources/CMakeLists.txt | 1 + resources/offset_partial.png | Bin 0 -> 870 bytes src/GEOMBase/GEOMBase.cxx | 17 ++ src/GEOMBase/GEOMBase.h | 1 + src/GEOMGUI/GEOM_images.ts | 4 + src/GEOMGUI/GEOM_msg_en.ts | 8 + src/GEOMGUI/GEOM_msg_fr.ts | 15 ++ src/GEOMImpl/GEOMImpl_ConformityDriver.cxx | 26 +- .../GEOMImpl_ITransformOperations.cxx | 40 ++- .../GEOMImpl_ITransformOperations.hxx | 8 +- src/GEOMImpl/GEOMImpl_OffsetDriver.cxx | 96 +++++-- src/GEOM_I/GEOM_IMeasureOperations_i.cc | 18 +- src/GEOM_I/GEOM_ITransformOperations_i.cc | 41 +++ src/GEOM_I/GEOM_ITransformOperations_i.hh | 4 + src/GEOM_SWIG/geomBuilder.py | 45 ++++ .../TransformationGUI_OffsetDlg.cxx | 252 +++++++++++++----- .../TransformationGUI_OffsetDlg.h | 18 +- 21 files changed, 504 insertions(+), 139 deletions(-) create mode 100644 doc/salome/gui/GEOM/images/transformation11a.png create mode 100644 resources/offset_partial.png 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 0000000000000000000000000000000000000000..f9e555bcabf8cb8b98b9be35cad7acb14cde0aea GIT binary patch literal 13585 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 Tr25~tCV-Nhx@@KN=fM8~w?Osi literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ced1d32dfbce1d4587a717f7849e9e7df74b99f9 GIT binary patch literal 870 zcmV-s1DX7ZP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf5dZ)S5dnW>Uy%R+0{clsK~y+Tt&~eh zR8bhm|1+Z~X~ow@g(JE!kEb4MH>MBoR1&V8KoyZ`fj-?=U+CDtzJFHTg=GGVPa!6x@O{aVN%|d1a!)sM%7lXQgPa{rl3x+JI*^x zQ&q04tdx0qd3=e$o*~t5?bE~R{@A)*=$<}@lfGsJ69luZ6h7SR6tdMEqojsp!KS<3}WCHe!vck3JMAm zHuYKf@EA=N@Q_=EC4YYBIm%I{7^w{G!D)WH%pFMqOGwm6EoTz&`{A&fd?ojT2dzY7=K%=%4J-V<>|d$nYF6 w1w*p2DvTow%*JY94(>IEE%Y2I2L=Ft0kR^coDSSZApigX07*qoM6N<$g0&Zp>;M1& literal 0 HcmV?d00001 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(); }; -- 2.39.2